Ejemplo n.º 1
0
    def to_python(self, value):
        if not value:
            return None

        if isinstance(value, IPAddress):
            return value

        # netaddr is a bit too liberal with what it accepts as a valid IP address. For example, '1.2.3' will become
        # IPAddress('1.2.0.3'). Here, we employ Django's built-in IPv4 and IPv6 address validators as a sanity check.
        try:
            validate_ipv4_address(value)
        except ValidationError:
            try:
                validate_ipv6_address(value)
            except ValidationError:
                raise ValidationError(
                    "Invalid IPv4/IPv6 address format: {}".format(value))

        try:
            return IPAddress(value)
        except ValueError:
            raise ValidationError(
                'This field requires an IP address without a mask.')
        except AddrFormatError:
            raise ValidationError(
                "Please specify a valid IPv4 or IPv6 address.")
Ejemplo n.º 2
0
def get_ip(request):
    """
    Retrieves the remote IP address from the request data.  If the user is
    behind a proxy, they may have a comma-separated list of IP addresses, so
    we need to account for that.  In such a case, only the first IP in the
    list will be retrieved.  Also, some hosts that use a proxy will put the
    REMOTE_ADDR into HTTP_X_FORWARDED_FOR.  This will handle pulling back the
    IP from the proper place.

    **NOTE** This function was taken from django-tracking (MIT LICENSE)
             http://code.google.com/p/django-tracking/
    """

    # if neither header contain a value, just use local loopback
    ip_address = request.META.get("HTTP_X_FORWARDED_FOR", request.META.get("REMOTE_ADDR", "127.0.0.1"))
    if ip_address:
        # make sure we have one and only one IP
        try:
            validate_ipv4_address(ip_address)
        except ValidationError:
            try:
                validate_ipv6_address(ip_address)
            except ValidationError:
                ip_address = "10.0.0.1"
    else:
        ip_address = "10.0.0.1"

    return ip_address
Ejemplo n.º 3
0
def validate_and_normalize_ip(ip_address, ip_type):
    """
    Validate and normalize the given IP address

    :param ip_address: the IP address to validate and normalize
    :type ip_address: str
    :param ip_type: the type of the IP address
    :type ip_type: str
    :returns: tuple: (Valid normalized IP, Error message)
    """

    cleaned = None
    if ip_type in (IPTypes.IPV4_SUBNET, IPTypes.IPV6_SUBNET):
        try:
            if '/' not in ip_address:
                raise ValidationError("")
            cidr_parts = ip_address.split('/')
            if int(cidr_parts[1]) < 0 or int(cidr_parts[1]) > 128:
                raise ValidationError("")
            if ':' not in cidr_parts[0] and int(cidr_parts[1]) > 32:
                raise ValidationError("")
            ip_address = cidr_parts[0]
        except (ValidationError, ValueError):
            return ("", "Invalid CIDR address")

    if ip_type in (IPTypes.IPV4_ADDRESS, IPTypes.IPV4_SUBNET):
        try:
            validate_ipv4_address(ip_address)

            # Remove leading zeros
            cleaned = []
            for octet in ip_address.split('.'):
                cleaned.append(octet.lstrip('0') or '0')
            cleaned = '.'.join(cleaned)
        except ValidationError:
            if ip_type == IPTypes.IPV4_ADDRESS:
                return ("", "Invalid IPv4 address")
            else:
                return ("", "Invalid IPv4 CIDR address")

    if ip_type in (IPTypes.IPV6_ADDRESS, IPTypes.IPV6_SUBNET):
        try:
            validate_ipv6_address(ip_address)

            # Replaces the longest continuous zero-sequence with "::" and
            # removes leading zeroes and makes sure all hextets are lowercase.
            cleaned = clean_ipv6_address(ip_address)
        except ValidationError:
            if ip_type == IPTypes.IPV6_ADDRESS:
                return ("", "Invalid IPv6 address")
            else:
                return ("", "Invalid IPv6 CIDR address")

    if not cleaned:
        return ("", "Invalid IP type.")
    elif ip_type in (IPTypes.IPV4_SUBNET, IPTypes.IPV6_SUBNET):
        return (cleaned + '/' + cidr_parts[1], "")
    else:
        return (cleaned, "")
Ejemplo n.º 4
0
def validate_and_normalize_ip(ip_address, ip_type):
    """
    Validate and normalize the given IP address

    :param ip_address: the IP address to validate and normalize
    :type ip_address: str
    :param ip_type: the type of the IP address
    :type ip_type: str
    :returns: tuple: (Valid normalized IP, Error message)
    """

    cleaned = None
    if "cidr" in ip_type:
        try:
            if "/" not in ip_address:
                raise ValidationError("")
            cidr_parts = ip_address.split("/")
            if int(cidr_parts[1]) < 0 or int(cidr_parts[1]) > 128:
                raise ValidationError("")
            if ":" not in cidr_parts[0] and int(cidr_parts[1]) > 32:
                raise ValidationError("")
            ip_address = cidr_parts[0]
        except (ValidationError, ValueError):
            return ("", "Invalid CIDR address")

    if "Address - ipv4" in ip_type or "cidr" in ip_type:
        try:
            validate_ipv4_address(ip_address)

            # Remove leading zeros
            cleaned = []
            for octet in ip_address.split("."):
                cleaned.append(octet.lstrip("0") or "0")
            cleaned = ".".join(cleaned)
        except ValidationError:
            if "cidr" not in ip_type:
                return ("", "Invalid IPv4 address")
            else:
                ip_type = "cidr_ipv6"

    if "Address - ipv6" in ip_type or ip_type == "cidr_ipv6":
        try:
            validate_ipv6_address(ip_address)

            # Replaces the longest continuous zero-sequence with "::" and
            # removes leading zeroes and makes sure all hextets are lowercase.
            cleaned = clean_ipv6_address(ip_address)
        except ValidationError:
            if "cidr" in ip_type:
                return ("", "Invalid CIDR address")
            else:
                return ("", "Invalid IPv6 address")

    if not cleaned:
        return ("", "Invalid IP type.")
    elif "cidr" in ip_type:
        return (cleaned + "/" + cidr_parts[1], "")
    else:
        return (cleaned, "")
Ejemplo n.º 5
0
def validate_and_normalize_ip(ip_address, ip_type):
    """
    Validate and normalize the given IP address

    :param ip_address: the IP address to validate and normalize
    :type ip_address: str
    :param ip_type: the type of the IP address
    :type ip_type: str
    :returns: tuple: (Valid normalized IP, Error message)
    """

    cleaned = None
    if ip_type in (IPTypes.IPV4_SUBNET, IPTypes.IPV6_SUBNET):
        try:
            if '/' not in ip_address:
                raise ValidationError("")
            cidr_parts = ip_address.split('/')
            if int(cidr_parts[1]) < 0 or int(cidr_parts[1]) > 128:
                raise ValidationError("")
            if ':' not in cidr_parts[0] and int(cidr_parts[1]) > 32:
                raise ValidationError("")
            ip_address = cidr_parts[0]
        except (ValidationError, ValueError):
            return ("", "Invalid CIDR address")

    if ip_type in (IPTypes.IPV4_ADDRESS, IPTypes.IPV4_SUBNET):
        try:
            validate_ipv4_address(ip_address)

            # Remove leading zeros
            cleaned = []
            for octet in ip_address.split('.'):
                cleaned.append(octet.lstrip('0') or '0')
            cleaned = '.'.join(cleaned)
        except ValidationError:
            if ip_type == IPTypes.IPV4_ADDRESS:
                return ("", "Invalid IPv4 address")
            else:
                return ("", "Invalid IPv4 CIDR address")

    if ip_type in (IPTypes.IPV6_ADDRESS, IPTypes.IPV6_SUBNET):
        try:
            validate_ipv6_address(ip_address)

            # Replaces the longest continuous zero-sequence with "::" and
            # removes leading zeroes and makes sure all hextets are lowercase.
            cleaned = clean_ipv6_address(ip_address)
        except ValidationError:
            if ip_type == IPTypes.IPV6_ADDRESS:
                return ("", "Invalid IPv6 address")
            else:
                return ("", "Invalid IPv6 CIDR address")

    if not cleaned:
        return ("", "Invalid IP type.")
    elif ip_type in (IPTypes.IPV4_SUBNET, IPTypes.IPV6_SUBNET):
        return (cleaned + '/' + cidr_parts[1], "")
    else:
        return (cleaned, "")
Ejemplo n.º 6
0
def ip_validator(ip):
    try:
        validate_ipv4_address(ip)
    except ValidationError:
        try:
            validate_ipv6_address(ip)
        except ValidationError:
            raise ValidationError('Invalid IP address')
Ejemplo n.º 7
0
 def clean_content(self):
     """Ensures that content is an IPv6 address."""
     content = self.cleaned_data.get('content')
     try:
         validate_ipv6_address(content)
     except ValidationError:
         raise forms.ValidationError("""Content should contain an IPv6 address""")
     else:
         return content
Ejemplo n.º 8
0
 def clean_content(self):
     """Clean content based on selected type"""
     data = self.cleaned_data.get('content')
     _type = self.cleaned_data.get('type')
     if _type == 'A':
         validate_ipv4_address(data)
     elif _type == 'AAAA':
         validate_ipv6_address(data)
     return data
Ejemplo n.º 9
0
 def clean_content(self):
     """Ensures that content is an IPv6 address."""
     content = self.cleaned_data.get('content')
     try:
         validate_ipv6_address(content)
     except ValidationError:
         raise forms.ValidationError("""Content should contain an IPv6 address""")
     else:
         return content
Ejemplo n.º 10
0
 def inner(value):
     try:
         validate_ipv4_address(value)
     except ValidationError:
         try:
             validate_ipv6_address(value)
         except ValidationError:
             raise argparse.ArgumentTypeError(
                 'Enter a valid IPv4 or IPv6 address')
     return value
Ejemplo n.º 11
0
 def validate_ipv46_address(self, value):
     from django.core.validators import validate_ipv4_address, validate_ipv6_address
     try:
         validate_ipv4_address(value)
         return 'ipv4'
     except ValidationError:
         try:
             validate_ipv6_address(value)
             return 'ipv6'
         except ValidationError:
             raise ValidationError(_('Enter a valid IPv4 or IPv6 address.'), code='invalid')
Ejemplo n.º 12
0
 def validate_ipv46_address(self, value):
     from django.core.validators import validate_ipv4_address, validate_ipv6_address
     try:
         validate_ipv4_address(value)
         return 'ipv4'
     except ValidationError:
         try:
             validate_ipv6_address(value)
             return 'ipv6'
         except ValidationError:
             raise ValidationError(_('Enter a valid IPv4 or IPv6 address.'),
                                   code='invalid')
    def test_filling_IPAddressField(self):
        obj = baker.make(models.DummyGenericIPAddressFieldModel)
        field = models.DummyGenericIPAddressFieldModel._meta.get_field('ipv4_field')
        assert isinstance(field, fields.GenericIPAddressField)
        assert isinstance(obj.ipv4_field, str)

        validate_ipv4_address(obj.ipv4_field)

        if hasattr(obj, 'ipv6_field'):
            assert isinstance(obj.ipv6_field, str)
            assert isinstance(obj.ipv46_field, str)

            validate_ipv6_address(obj.ipv6_field)
            validate_ipv46_address(obj.ipv46_field)
Ejemplo n.º 14
0
 def clean(self):
   cleaned_data = super(EditRecordForm, self).clean()
   name = cleaned_data.get('name')
   domain = Domain.objects.get(id=cleaned_data.get('domain'))
   type = cleaned_data.get('type')
   content = cleaned_data.get('content')
   mac = cleaned_data.get('mac')
   #fix a bug where validate_ipv6_address cannot process NoneType
   #So instead of passing None we pass 1 witch is invalid as well
   if content == None:
     content = '1'
   if type == 'A' or type == 'AAAA':
     ip_valid = True
   else:
     ip_valid = False
   if type == 'A':
     try: 
       validate_ipv4_address(content)
     except ValidationError:
       self.errors['content'] = self.error_class(['Not an IPv4 address'])
       ip_valid = False
     except AddrFormatError:
       self.errors['content'] = self.error_class(['Not an IPv4 address'])
       ip_valid = False
   if type == 'AAAA' and ip_valid:
     try: 
       validate_ipv6_address(content)
     except ValidationError:
       self.errors['content'] = self.error_class(['Not an IPv6 address'])
       ip_valid = False
   ranges = Range.objects.all()
   found = False
   if ip_valid:
     for range in ranges:
       if IPAddress(content) >= IPNetwork(range.cidr).network and IPAddress(content) <= IPNetwork(range.cidr).broadcast:
         found = True
         break
   if mac:
     if not re.match("[0-9a-f]{2}([\.\-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", mac.lower()):
       self.errors['mac'] = self.error_class(['Not a MAC address'])
       del cleaned_data['mac']
     m = re.sub("[.:-]", "", mac)
     m = m.lower()
     mac = "%s-%s-%s-%s-%s-%s" % (m[0:2], m[2:4], m[4:6], m[6:8], m[8:10], m[10:])
     cleaned_data['mac'] = mac
   if not found and ip_valid:
     self.errors['content'] = self.error_class(['IP is not within a known range'])
     ip_valid = False
   return cleaned_data
Ejemplo n.º 15
0
def validate_ip_address(value):
    """
    The method validating ipv4 and ipv6 address.
    """

    error_message = None
    try:
        validate_ipv4_address(value)
    except Exception as error:
        error_message = error
    if error_message:
        try:
            validate_ipv6_address(value)
        except Exception as error:
            raise serializers.ValidationError("{} or {}".format(
                error, error_message))
Ejemplo n.º 16
0
def get_ip_address_from_request(request):
    """ Makes the best attempt to get the client's real IP or return the loopback """
    
    # Would rather rely on middleware to set up a good REMOTE_ADDR than try to get
    # fancy here. Also, just use django's built in ipv4/ipv6 normalization logic
    ip_address = request.META.get('REMOTE_ADDR', 'bad address')
    try:
        validate_ipv4_address(ip_address)
    except:
        try:
            validate_ipv6_address(ip_address)
            ip_address = clean_ipv6_address(ip_address, True)
        except:
            log.error("Could not parse address {0}".format(ip_address))
            ip_address = "127.0.0.1"
        
    return ip_address
Ejemplo n.º 17
0
    def __call__(self, value):
        if '://' in value:
            scheme = value.split('://')[0].lower()
        else:
            scheme = None

        # отключаю проверку схемы url
        if scheme:
            if scheme not in self.schemes:
                raise ValidationError(self.message, code=self.code)

        try:
            super(URLValidator, self).__call__(value)
        except ValidationError as e:
            if value:
                try:
                    scheme, netloc, path, query, fragment = urlsplit(value)
                except ValueError:  # for example, "Invalid IPv6 URL"
                    raise ValidationError(self.message, code=self.code)
                try:
                    netloc = netloc.encode('idna').decode(
                        'ascii')  # IDN -> ACE
                except UnicodeError:  # invalid domain part
                    raise e
                url = urlunsplit((scheme, netloc, path, query, fragment))
                super().__call__(url)
            else:
                raise
        else:
            # Now verify IPv6 in the netloc part
            host_match = re.search(r'^\[(.+)\](?::\d{2,5})?$',
                                   urlsplit(value).netloc)
            if host_match:
                potential_ip = host_match.groups()[0]
                try:
                    validate_ipv6_address(potential_ip)
                except ValidationError:
                    raise ValidationError(self.message, code=self.code)

        # The maximum length of a full host name is 253 characters per RFC 1034
        # section 3.1. It's defined to be 255 bytes or less, but this includes
        # one byte for the length of the name and one byte for the trailing dot
        # that's used to indicate absolute names in DNS.
        if len(urlsplit(value).netloc) > 253:
            raise ValidationError(self.message, code=self.code)
Ejemplo n.º 18
0
    def test_filling_IPAddressField(self):
        try:
            from test.generic.models import DummyGenericIPAddressFieldModel as IPModel
        except ImportError:
            from test.generic.models import DummyIPAddressFieldModel as IPModel

        obj = mommy.make(IPModel)
        field = IPModel._meta.get_field('ipv4_field')
        self.assertIsInstance(field, GenericIPAddressField)
        self.assertIsInstance(obj.ipv4_field, string_types)

        validate_ipv4_address(obj.ipv4_field)

        if hasattr(obj, 'ipv6_field'):
            self.assertIsInstance(obj.ipv6_field, string_types)
            self.assertIsInstance(obj.ipv46_field, string_types)

            validate_ipv6_address(obj.ipv6_field)
            validate_ipv46_address(obj.ipv46_field)
Ejemplo n.º 19
0
    def test_filling_IPAddressField(self):
        try:
            from test.generic.models import DummyGenericIPAddressFieldModel as IPModel
        except ImportError:
            from test.generic.models import DummyIPAddressFieldModel as IPModel

        obj = mommy.make(IPModel)
        field = IPModel._meta.get_field('ipv4_field')
        self.assertIsInstance(field, GenericIPAddressField)
        self.assertIsInstance(obj.ipv4_field, string_types)

        validate_ipv4_address(obj.ipv4_field)

        if hasattr(obj, 'ipv6_field'):
            self.assertIsInstance(obj.ipv6_field, string_types)
            self.assertIsInstance(obj.ipv46_field, string_types)

            validate_ipv6_address(obj.ipv6_field)
            validate_ipv46_address(obj.ipv46_field)
Ejemplo n.º 20
0
        def __call__(self, value):
            # Check first if the scheme is valid
            scheme = value.split('://')[0].lower()
            if scheme not in self.schemes:
                raise ValidationError(self.message, code=self.code)

            # Then check full URL
            try:
                super(URLValidator, self).__call__(value)
            except ValidationError as e:
                # Trivial case failed. Try for possible IDN domain
                if value:
                    try:
                        scheme, netloc, path, query, fragment = urlsplit(value)
                    except ValueError:  # for example, "Invalid IPv6 URL"
                        raise ValidationError(self.message, code=self.code)
                    try:
                        netloc = netloc.encode('idna').decode('ascii')  # IDN -> ACE
                    except UnicodeError:  # invalid domain part
                        raise e
                    url = urlunsplit((scheme, netloc, path, query, fragment))
                    super(URLValidator, self).__call__(url)
                else:
                    raise
            else:
                # Now verify IPv6 in the netloc part
                host_match = re.search(r'^\[(.+)\](?::\d{2,5})?$', urlsplit(value).netloc)
                if host_match:
                    potential_ip = host_match.groups()[0]
                    try:
                        validate_ipv6_address(potential_ip)
                    except ValidationError:
                        raise ValidationError(self.message, code=self.code)

            # The maximum length of a full host name is 253 characters per RFC 1034
            # section 3.1. It's defined to be 255 bytes or less, but this includes
            # one byte for the length of the name and one byte for the trailing dot
            # that's used to indicate absolute names in DNS.
            if len(urlsplit(value).netloc) > 253:
                raise ValidationError(self.message, code=self.code)
Ejemplo n.º 21
0
 def ipv6(self):
     try:
         return True if validate_ipv6_address(
             self.__input) is None else False
     except Exception:
         return False
Ejemplo n.º 22
0
def ip_add_update(ip_address,
                  ip_type,
                  source=None,
                  source_method=None,
                  source_reference=None,
                  campaign=None,
                  confidence='low',
                  analyst=None,
                  is_add_indicator=False,
                  indicator_reference=None,
                  bucket_list=None,
                  ticket=None,
                  is_validate_only=False,
                  cache={}):
    """
    Add/update an IP address.

    :param ip_address: The IP to add/update.
    :type ip_address: str
    :param ip_type: The type of IP this is.
    :type ip_type: str
    :param source: Name of the source which provided this information.
    :type source: str
    :param source_method: Method of acquiring this data.
    :type source_method: str
    :param source_reference: A reference to this data.
    :type source_reference: str
    :param campaign: A campaign to attribute to this IP address.
    :type campaign: str
    :param confidence: Confidence level in the campaign attribution.
    :type confidence: str ("low", "medium", "high")
    :param analyst: The user adding/updating this IP.
    :type analyst: str
    :param is_add_indicator: Also add an Indicator for this IP.
    :type is_add_indicator: bool
    :param indicator_reference: Reference for the indicator.
    :type indicator_reference: str
    :param bucket_list: Buckets to assign to this IP.
    :type bucket_list: str
    :param ticket: Ticket to assign to this IP.
    :type ticket: str
    :param is_validate_only: Only validate, do not add/update.
    :type is_validate_only: bool
    :param cache: Cached data, typically for performance enhancements
                  during bulk operations.
    :type cache: dict
    :returns: dict with keys:
              "success" (boolean),
              "message" (str),
              "object" (if successful) :class:`crits.ips.ip.IP`
    """

    if not source:
        return {"success": False, "message": "Missing source information."}

    if "Address - ipv4" in ip_type:
        try:
            validate_ipv4_address(ip_address)
        except ValidationError:
            return {"success": False, "message": "Invalid IPv4 address."}
    elif "Address - ipv6" in ip_type:
        try:
            validate_ipv6_address(ip_address)
        except ValidationError:
            return {"success": False, "message": "Invalid IPv6 address."}
    elif "cidr" in ip_type:
        try:
            if '/' not in ip_address:
                raise ValidationError("Missing slash.")
            cidr_parts = ip_address.split('/')
            if int(cidr_parts[1]) < 0 or int(cidr_parts[1]) > 128:
                raise ValidationError("Invalid mask.")
            if ':' not in cidr_parts[0] and int(cidr_parts[1]) > 32:
                raise ValidationError("Missing colon.")
            validate_ipv46_address(cidr_parts[0])
        except (ValidationError, ValueError) as cidr_error:
            return {
                "success": False,
                "message": "Invalid CIDR address: %s" % cidr_error
            }
    else:
        return {"success": False, "message": "Invalid IP type."}

    retVal = {}
    is_item_new = False

    ip_object = None
    cached_results = cache.get(form_consts.IP.CACHED_RESULTS)

    if cached_results != None:
        ip_object = cached_results.get(ip_address)
    else:
        ip_object = IP.objects(ip=ip_address).first()

    if not ip_object:
        ip_object = IP()
        ip_object.ip = ip_address
        ip_object.ip_type = ip_type
        is_item_new = True

        if cached_results != None:
            cached_results[ip_address] = ip_object

    if isinstance(source, basestring):
        source = [
            create_embedded_source(source,
                                   reference=source_reference,
                                   method=source_method,
                                   analyst=analyst)
        ]

    if isinstance(campaign, basestring):
        c = EmbeddedCampaign(name=campaign,
                             confidence=confidence,
                             analyst=analyst)
        campaign = [c]

    if campaign:
        for camp in campaign:
            ip_object.add_campaign(camp)

    if source:
        for s in source:
            ip_object.add_source(s)
    else:
        return {"success": False, "message": "Missing source information."}

    if bucket_list:
        ip_object.add_bucket_list(bucket_list, analyst)

    if ticket:
        ip_object.add_ticket(ticket, analyst)

    resp_url = reverse('crits.ips.views.ip_detail', args=[ip_object.ip])

    if is_validate_only == False:
        ip_object.save(username=analyst)

        #set the URL for viewing the new data
        if is_item_new == True:
            retVal['message'] = ('Success! Click here to view the new IP: '
                                 '<a href="%s">%s</a>' %
                                 (resp_url, ip_object.ip))
        else:
            message = ('Updated existing IP: '
                       '<a href="%s">%s</a>' % (resp_url, ip_object.ip))
            retVal['message'] = message
            retVal['status'] = form_consts.Status.DUPLICATE
            retVal['warning'] = message
    elif is_validate_only == True:
        if ip_object.id != None and is_item_new == False:
            message = ('Warning: IP already exists: '
                       '<a href="%s">%s</a>' % (resp_url, ip_object.ip))
            retVal['message'] = message
            retVal['status'] = form_consts.Status.DUPLICATE
            retVal['warning'] = message

    if is_add_indicator:
        from crits.indicators.handlers import handle_indicator_ind
        handle_indicator_ind(ip_address,
                             source,
                             indicator_reference,
                             ip_type,
                             analyst,
                             source_method,
                             add_domain=False,
                             add_relationship=True,
                             bucket_list=bucket_list,
                             ticket=ticket,
                             cache=cache)

    # run ip triage
    if is_item_new and is_validate_only == False:
        ip_object.reload()
        run_triage(ip_object, analyst)

    retVal['success'] = True
    retVal['object'] = ip_object

    return retVal
Ejemplo n.º 23
0
    def is_valid(self, bundle, request=None):

        if not bundle.data:
            return {'__all__': 'No parameters passed'}

        errors = {}

        record_type = int(bundle.data['record_type'])
        host = bundle.data['host']
        answer = bundle.data['answer']
        zone = bundle.data['zone']

        if 'record_id' in bundle.data:
            record_id = int(bundle.data['record_id'])
        else:
            record_id = False

        view, args, kwargs = resolve(zone)
        zone_pk = kwargs['pk']

        record_type_text = dict(record_type_choices).get(record_type)

        if self.if_duplicate(host, answer, zone_pk, record_id):
            errors['duplicate'] = ['Duplicated host and answer']

        if self.if_same_host(host, record_type_text, zone_pk, record_id):
            errors['duplicate'] = ['Same host detected. RFC violation.']

        if record_type_text == 'A':
            try:
                validate_ipv4_address(answer)
            except:
                errors['answer'] = ['Should be IPv4 address']

            if not self.if_hostname(host):
                errors['host'] = ['Should be valid hostname']

        elif record_type_text == 'AAAA':
            try:
                validate_ipv6_address(answer)
            except:
                errors['answer'] = ['Should be IPv6 address']

            if not self.if_hostname(host) or host == '@':
                errors['host'] = ['Should be valid hostname']

        elif record_type_text == 'CNAME':

            if not self.if_fqdn(answer):
                errors['answer'] = ['Should be valid FQDN']

            if not self.if_hostname(host):
                errors['host'] = ['Should be valid hostname']
           
        elif record_type_text == 'NS':

            if not self.if_fqdn(answer):
                errors['answer'] = ['Should be valid FQDN']

            if not self.if_hostname(host):
                errors['host'] = ['Should be valid hostname']

        elif record_type_text == 'MX':
        
            if not self.if_fqdn(answer):
                errors['answer'] = ['Should be valid FQDN']

            if not self.if_hostname(host):
                errors['host'] = ['Should be valid hostname']

        elif record_type_text == 'PTR':
        
            if not self.if_fqdn(answer):
                errors['answer'] = ['Should be valid FQDN']
 
        return errors
Ejemplo n.º 24
0
    def clean(self):
        '''Handles the validation logic for the domain record form.

        Validation gets pretty complicated due to the fact that the different
        record types (A, AAAA, MX, etc) have different requirements for
        each of the fields.
        '''

        cleaned_data = super(RecordForm, self).clean()
        record_type = cleaned_data['type']
        domain_name = cleaned_data['domain_name']

        #  Name field
        if self._is_field_blank(cleaned_data, 'name'):
            if record_type in ['A', 'AAAA', 'CNAME', 'SRV', 'TXT', 'PTR']:
                self._add_required_field_error('name')
            elif record_type == 'MX':
                cleaned_data['name'] = domain_name
        else:
            if record_type == 'SRV':
                if not re.match(SRV_NAME_REGEX, cleaned_data['name']):
                    self._add_field_error('name', _('Enter a valid SRV name'))
                else:
                    cleaned_data['name'] += domain_name
            else:
                if not re.match(WILDCARD_DOMAIN_NAME_REGEX,
                                cleaned_data['name']):
                    self._add_field_error('name', _('Enter a valid hostname'))
                elif not cleaned_data['name'].endswith(domain_name):
                    self._add_field_error(
                        'name', _('Name must be in the current domain'))

        # Data field
        if self._is_field_blank(cleaned_data, 'data'):
            if record_type in ['A', 'AAAA', 'CNAME', 'MX', 'SRV']:
                self._add_required_field_error('data')
        else:
            if record_type == 'A':
                try:
                    validators.validate_ipv4_address(cleaned_data['data'])
                except ValidationError:
                    self._add_field_error('data',
                                          _('Enter a valid IPv4 address'))

            elif record_type == 'AAAA':
                try:
                    validators.validate_ipv6_address(cleaned_data['data'])
                except ValidationError:
                    self._add_field_error('data',
                                          _('Enter a valid IPv6 address'))

            elif record_type in ['CNAME', 'MX', 'PTR']:
                if not re.match(DOMAIN_NAME_REGEX, cleaned_data['data']):
                    self._add_field_error('data', _('Enter a valid hostname'))

            elif record_type == 'SRV':
                if not re.match(SRV_DATA_REGEX, cleaned_data['data']):
                    self._add_field_error('data',
                                          _('Enter a valid SRV record'))

        # Txt field
        if self._is_field_blank(cleaned_data, 'txt'):
            if record_type == 'TXT':
                self._add_required_field_error('txt')
        else:
            if record_type == 'TXT':
                cleaned_data['data'] = cleaned_data['txt']

        if record_type == 'SSHFP':
            if not re.match(SSHFP_DATA_REGEX, cleaned_data['txt']):
                self._add_field_error('txt', _('Enter a valid SSHFP record'))
            cleaned_data['data'] = cleaned_data['txt']

        cleaned_data.pop('txt')

        # Priority field
        if self._is_field_blank(cleaned_data, 'priority'):
            if record_type in ['MX', 'SRV']:
                self._add_required_field_error('priority')

        # Rename 'id' to 'record_id'
        if 'id' in cleaned_data:
            cleaned_data['record_id'] = cleaned_data.pop('id')

        # Remove domain_name
        cleaned_data.pop('domain_name')

        return cleaned_data
Ejemplo n.º 25
0
    def clean(self):
        '''Handles the validation logic for the domain record form.

        Validation gets pretty complicated due to the fact that the different
        record types (A, AAAA, MX, etc) have different requirements for
        each of the fields.
        '''

        cleaned_data = super(RecordForm, self).clean()
        record_type = cleaned_data['type']
        domain_name = cleaned_data['domain_name']

        #  Name field
        if self._is_field_blank(cleaned_data, 'name'):
            if record_type in ['A', 'AAAA', 'CNAME', 'SRV', 'TXT', 'PTR']:
                self._add_required_field_error('name')
            elif record_type == 'MX':
                cleaned_data['name'] = domain_name
        else:
            if record_type == 'SRV':
                if not re.match(SRV_NAME_REGEX, cleaned_data['name']):
                    self._add_field_error('name', _('Enter a valid SRV name'))
                else:
                    cleaned_data['name'] += domain_name
            else:
                if not re.match(WILDCARD_DOMAIN_NAME_REGEX,
                                cleaned_data['name']):
                    self._add_field_error('name', _('Enter a valid hostname'))
                elif not cleaned_data['name'].endswith(domain_name):
                    self._add_field_error(
                        'name',
                        _('Name must be in the current domain'))

        # Data field
        if self._is_field_blank(cleaned_data, 'data'):
            if record_type in ['A', 'AAAA', 'CNAME', 'MX', 'SRV']:
                self._add_required_field_error('data')
        else:
            if record_type == 'A':
                try:
                    validators.validate_ipv4_address(cleaned_data['data'])
                except ValidationError:
                    self._add_field_error('data',
                                          _('Enter a valid IPv4 address'))

            elif record_type == 'AAAA':
                try:
                    validators.validate_ipv6_address(cleaned_data['data'])
                except ValidationError:
                    self._add_field_error('data',
                                          _('Enter a valid IPv6 address'))

            elif record_type in ['CNAME', 'MX', 'PTR']:
                if not re.match(DOMAIN_NAME_REGEX, cleaned_data['data']):
                    self._add_field_error('data', _('Enter a valid hostname'))

            elif record_type == 'SRV':
                if not re.match(SRV_DATA_REGEX, cleaned_data['data']):
                    self._add_field_error('data',
                                          _('Enter a valid SRV record'))

        # Txt field
        if self._is_field_blank(cleaned_data, 'txt'):
            if record_type == 'TXT':
                self._add_required_field_error('txt')
        else:
            if record_type == 'TXT':
                cleaned_data['data'] = cleaned_data['txt']

        if record_type == 'SSHFP':
            if not re.match(SSHFP_DATA_REGEX, cleaned_data['txt']):
                self._add_field_error('txt',
                                      _('Enter a valid SSHFP record'))
            cleaned_data['data'] = cleaned_data['txt']

        cleaned_data.pop('txt')

        # Priority field
        if self._is_field_blank(cleaned_data, 'priority'):
            if record_type in ['MX', 'SRV']:
                self._add_required_field_error('priority')

        # Rename 'id' to 'record_id'
        if 'id' in cleaned_data:
            cleaned_data['record_id'] = cleaned_data.pop('id')

        # Remove domain_name
        cleaned_data.pop('domain_name')

        return cleaned_data
Ejemplo n.º 26
0
def add_object(type_, oid, object_type, name, source, method,
               reference, analyst, value=None, file_=None,
               add_indicator=False, get_objects=True,
               obj=None, is_sort_relationships=False,
               is_validate_only=False, is_validate_locally=False, cache={}):
    """
    Add an object to the database.

    :param type_: The top-level object type.
    :type type_: str
    :param oid: The ObjectId of the top-level object.
    :type oid: str
    :param object_type: The type of the ObjectType being added.
    :type object_type: str
    :param name: The name of the ObjectType being added.
    :type name: str
    :param source: The name of the source adding this object.
    :type source: str
    :param method: The method for this object.
    :type method: str
    :param reference: The reference for this object.
    :type reference: str
    :param analyst: The user adding this object.
    :type analyst: str
    :param value: The value of the object.
    :type value: str
    :param file_: The file if the object is a file upload.
    :type file_: file handle.
    :param add_indicator: Also add an indicator for this object.
    :type add_indicator: bool
    :param get_objects: Return the formatted list of objects when completed.
    :type get_object: bool
    :param obj: The CRITs top-level object we are adding objects to.
                This is an optional parameter used mainly for performance
                reasons (by not querying mongo if we already have the
                top level-object).
    :type obj: :class:`crits.core.crits_mongoengine.CritsBaseAttributes`
    :param is_validate_only: Only validate, do not add.
    :type is_validate_only: bool
    :param is_validate_locally: Only validate, do not add.
    :type is_validate_locally: bool
    :param cache: Cached data, typically for performance enhancements
                  during bulk operations.
    :type cache: dict
    :returns: dict with keys:
              "success" (boolean),
              "message" (str),
              "objects" (list),
              "relationships" (list)
    """

    results = {}

    if oid == None:
        oid = ""

    if obj == None:
        obj = class_from_id(type_, oid)

    if not obj:
        if is_validate_locally == True:
            # TODO: Perform some form of validation
            results['success'] = True
            return results
        else:
            results['message'] = "Could not find item to add object to."
            results['success'] = False
            return results

    if name == "URL" and "://" not in value.split('.')[0]:
        return {"success" : False, "message" : "URI - URL must contain protocol prefix (e.g. http://, https://, ftp://)"}
    elif object_type == "Address":
        if "ipv4" in name:
            try:
                validate_ipv4_address(value)
            except DjangoValidationError:
                return {"success" : False, "message" : "Invalid IPv4 address. "}
        elif "ipv6" in name:
            try:
                validate_ipv6_address(value)
            except DjangoValidationError:
                return {"success" : False, "message" : "Invalid IPv6 address. "}
        elif "cidr" in name:
            try:
                if '/' not in value:
                    raise ValidationError("")
                cidr_parts = value.split('/')
                if int(cidr_parts[1]) < 0 or int(cidr_parts[1]) > 128:
                    raise ValidationError("")
                if ':' not in cidr_parts[0] and int(cidr_parts[1]) > 32:
                    raise ValidationError("")
                validate_ipv46_address(cidr_parts[0])
            except (ValidationError, ValueError) as cidr_error:
                return {"success" : False, "message" : "Invalid CIDR address. "}

    try:
        cur_len = len(obj.obj)
        if file_:
            data = file_.read()
            filename = file_.name
            md5sum = md5(data).hexdigest()
            value = md5sum
            reference = filename
        obj.add_object(object_type,
                       name,
                       value,
                       source,
                       method,
                       reference,
                       analyst)

        if is_validate_only == False:
            obj.save(username=analyst)

        new_len = len(obj.obj)
        if new_len > cur_len:
            results['message'] = "Object added successfully!"
            results['success'] = True
            if file_:
                # do we have a pcap?
                if data[:4] in ('\xa1\xb2\xc3\xd4',
                                '\xd4\xc3\xb2\xa1',
                                '\x0a\x0d\x0d\x0a'):
                    handle_pcap_file(filename,
                                     data,
                                     source,
                                     user=analyst,
                                     related_id=oid,
                                     related_type=type_)
                else:
                    #XXX: MongoEngine provides no direct GridFS access so we
                    #     need to use pymongo directly.
                    col = settings.COL_OBJECTS
                    grid = mongo_connector("%s.files" % col)
                    if grid.find({'md5': md5sum}).count() == 0:
                        put_file(filename, data, collection=col)
            if add_indicator and is_validate_only == False:
                from crits.indicators.handlers import handle_indicator_ind

                if object_type != name:
                    object_type = "%s - %s" % (object_type, name)

                campaign = obj.campaign if hasattr(obj, 'campaign') else None
                ind_res = handle_indicator_ind(value,
                                               source,
                                               reference,
                                               object_type,
                                               analyst,
                                               method,
                                               add_domain=True,
                                               campaign=campaign,
                                               cache=cache)

                if ind_res['success']:
                    ind = ind_res['object']
                    forge_relationship(left_class=obj,
                                       right_class=ind,
                                       rel_type="Related_To",
                                       analyst=analyst,
                                       get_rels=is_sort_relationships)
                else:
                    results['message'] = "Object was added, but failed to add Indicator." \
                                         "<br>Error: " + ind_res.get('message')

            if is_sort_relationships == True:
                if file_ or add_indicator:
                    # does this line need to be here?
                    # obj.reload()
                    results['relationships'] = obj.sort_relationships(analyst, meta=True)
                else:
                    results['relationships'] = obj.sort_relationships(analyst, meta=True)

        else:
            results['message'] = "Object already exists! [Type: " + object_type + "][Value: " + value + "] "
            results['success'] = False
        if (get_objects):
            results['objects'] = obj.sort_objects()

        results['id'] = str(obj.id)
        return results
    except ValidationError, e:
        return {'success': False,
                'message': str(e)}
Ejemplo n.º 27
0
 def __call__(self):
     try:
         validate_ipv6_address(self.content)
     except ValidationError as exc:
         raise RecordContentValidationError(exc.message)
Ejemplo n.º 28
0
 def validate(self, value):
     "Check if value consists only of valid IPv4s."
     # Use the parent's handling of required fields, etc.
     super(MultiIp6AddressField, self).validate(value)
     for ip in value:
         validate_ipv6_address(ip)
Ejemplo n.º 29
0
def ip_add_update(ip_address, ip_type, source=None, source_method=None,
                  source_reference=None, campaign=None, confidence='low',
                  analyst=None, is_add_indicator=False, indicator_reference=None,
                  bucket_list=None, ticket=None, is_validate_only=False, cache={}):
    """
    Add/update an IP address.

    :param ip_address: The IP to add/update.
    :type ip_address: str
    :param ip_type: The type of IP this is.
    :type ip_type: str
    :param source: Name of the source which provided this information.
    :type source: str
    :param source_method: Method of acquiring this data.
    :type source_method: str
    :param source_reference: A reference to this data.
    :type source_reference: str
    :param campaign: A campaign to attribute to this IP address.
    :type campaign: str
    :param confidence: Confidence level in the campaign attribution.
    :type confidence: str ("low", "medium", "high")
    :param analyst: The user adding/updating this IP.
    :type analyst: str
    :param is_add_indicator: Also add an Indicator for this IP.
    :type is_add_indicator: bool
    :param indicator_reference: Reference for the indicator.
    :type indicator_reference: str
    :param bucket_list: Buckets to assign to this IP.
    :type bucket_list: str
    :param ticket: Ticket to assign to this IP.
    :type ticket: str
    :param is_validate_only: Only validate, do not add/update.
    :type is_validate_only: bool
    :param cache: Cached data, typically for performance enhancements
                  during bulk operations.
    :type cache: dict
    :returns: dict with keys:
              "success" (boolean),
              "message" (str),
              "object" (if successful) :class:`crits.ips.ip.IP`
    """

    if not source:
        return {"success" : False, "message" : "Missing source information."}

    if "Address - ipv4" in ip_type:
        try:
            validate_ipv4_address(ip_address)
        except ValidationError:
            return {"success": False, "message": "Invalid IPv4 address."}
    elif "Address - ipv6" in ip_type:
        try:
            validate_ipv6_address(ip_address)
        except ValidationError:
            return {"success": False, "message": "Invalid IPv6 address."}
    elif "cidr" in ip_type:
        try:
            if '/' not in ip_address:
                raise ValidationError("Missing slash.")
            cidr_parts = ip_address.split('/')
            if int(cidr_parts[1]) < 0 or int(cidr_parts[1]) > 128:
                raise ValidationError("Invalid mask.")
            if ':' not in cidr_parts[0] and int(cidr_parts[1]) > 32:
                raise ValidationError("Missing colon.")
            validate_ipv46_address(cidr_parts[0])
        except (ValidationError, ValueError) as cidr_error:
            return {"success": False, "message": "Invalid CIDR address: %s" %
                                                  cidr_error}
    else:
        return {"success": False, "message": "Invalid IP type."}

    retVal = {}
    is_item_new = False

    ip_object = None
    cached_results = cache.get(form_consts.IP.CACHED_RESULTS)

    if cached_results != None:
        ip_object = cached_results.get(ip_address)
    else:
        ip_object = IP.objects(ip=ip_address).first()

    if not ip_object:
        ip_object = IP()
        ip_object.ip = ip_address
        ip_object.ip_type = ip_type
        is_item_new = True

        if cached_results != None:
            cached_results[ip_address] = ip_object

    if isinstance(source, basestring):
        source = [create_embedded_source(source,
                                         reference=source_reference,
                                         method=source_method,
                                         analyst=analyst)]

    if isinstance(campaign, basestring):
        c = EmbeddedCampaign(name=campaign, confidence=confidence, analyst=analyst)
        campaign = [c]

    if campaign:
        for camp in campaign:
            ip_object.add_campaign(camp)

    if source:
        for s in source:
            ip_object.add_source(s)
    else:
        return {"success" : False, "message" : "Missing source information."}

    if bucket_list:
        ip_object.add_bucket_list(bucket_list, analyst)

    if ticket:
        ip_object.add_ticket(ticket, analyst)

    resp_url = reverse('crits.ips.views.ip_detail', args=[ip_object.ip])

    if is_validate_only == False:
        ip_object.save(username=analyst)

        #set the URL for viewing the new data
        if is_item_new == True:
            retVal['message'] = ('Success! Click here to view the new IP: '
                                 '<a href="%s">%s</a>' % (resp_url, ip_object.ip))
        else:
            message = ('Updated existing IP: '
                                 '<a href="%s">%s</a>' % (resp_url, ip_object.ip))
            retVal['message'] = message
            retVal['status'] = form_consts.Status.DUPLICATE
            retVal['warning'] = message
    elif is_validate_only == True:
        if ip_object.id != None and is_item_new == False:
            message = ('Warning: IP already exists: '
                                 '<a href="%s">%s</a>' % (resp_url, ip_object.ip))
            retVal['message'] = message
            retVal['status'] = form_consts.Status.DUPLICATE
            retVal['warning'] = message

    if is_add_indicator:
        from crits.indicators.handlers import handle_indicator_ind
        handle_indicator_ind(ip_address,
                             source,
                             indicator_reference,
                             ip_type,
                             analyst,
                             source_method,
                             add_domain=False,
                             add_relationship=True,
                             bucket_list=bucket_list,
                             ticket=ticket,
                             cache=cache)

    # run ip triage
    if is_item_new and is_validate_only == False:
        ip_object.reload()
        run_triage(ip_object, analyst)

    retVal['success'] = True
    retVal['object'] = ip_object

    return retVal
Ejemplo n.º 30
0
    def clean(self):
        '''Handles the validation logic for the domain record form.

        Validation gets pretty complicated due to the fact that the different
        record types (A, AAAA, MX, etc) have different requirements for
        each of the fields.
        '''

        cleaned_data = super(RecordForm, self).clean()
        record_type = cleaned_data['type']
        domain_name = cleaned_data['domain_name']
        if limit_records_to_fips():
            ip_addr = cleaned_data.pop('ip_addr')
            if (record_type in ['AAAA', 'A'] and limit_records_to_fips()):
                cleaned_data['data'] = ip_addr

        #  Name field
        if self._is_field_blank(cleaned_data, 'name'):
            if record_type in ['CNAME', 'SRV']:
                self._add_required_field_error('name')
            elif record_type in ['MX', 'A', 'AAAA', 'TXT', 'PTR']:
                cleaned_data['name'] = domain_name
        else:
            if record_type == 'SRV':
                if not re.match(SRV_NAME_REGEX, cleaned_data['name']):
                    self._add_field_error('name', _('Enter a valid SRV name'))
                else:
                    cleaned_data['name'] += domain_name
            else:
                cleaned_data['name'] += "." + domain_name
                if not re.match(WILDCARD_DOMAIN_NAME_REGEX,
                                cleaned_data['name']):
                    self._add_field_error(
                        'name',
                        _('Enter a valid hostname. The '
                          'hostname should contain letters '
                          'and numbers, and be no more than '
                          '63 characters.'))
        # Data field
        if self._is_field_blank(cleaned_data, 'data'):
            if record_type in ['A', 'AAAA', 'CNAME', 'MX', 'SRV']:
                self._add_required_field_error('data')
        else:
            if record_type == 'A':
                try:
                    validators.validate_ipv4_address(cleaned_data['data'])
                except ValidationError:
                    self._add_field_error('data',
                                          _('Enter a valid IPv4 address'))

            elif record_type == 'AAAA':
                try:
                    validators.validate_ipv6_address(cleaned_data['data'])
                except ValidationError:
                    self._add_field_error('data',
                                          _('Enter a valid IPv6 address'))

            elif record_type in ['CNAME', 'MX', 'PTR']:
                if not re.match(DOMAIN_NAME_REGEX, cleaned_data['data']):
                    self._add_field_error('data', _('Enter a valid hostname'))

            elif record_type == 'SRV':
                if not re.match(SRV_DATA_REGEX, cleaned_data['data']):
                    self._add_field_error('data',
                                          _('Enter a valid SRV record'))

        # Txt field
        if self._is_field_blank(cleaned_data, 'txt'):
            if record_type == 'TXT':
                self._add_required_field_error('txt')
        else:
            if record_type == 'TXT':
                cleaned_data['data'] = cleaned_data['txt']

        if record_type == 'SSHFP':
            if not re.match(SSHFP_DATA_REGEX, cleaned_data['txt']):
                self._add_field_error('txt', _('Enter a valid SSHFP record'))
            cleaned_data['data'] = cleaned_data['txt']

        cleaned_data.pop('txt')

        # Priority field
        if self._is_field_blank(cleaned_data, 'priority'):
            if record_type in ['MX', 'SRV']:
                self._add_required_field_error('priority')

        # Rename 'id' to 'record_id'
        if 'id' in cleaned_data:
            cleaned_data['record_id'] = cleaned_data.pop('id')

        # Remove domain_name
        cleaned_data.pop('domain_name')

        return cleaned_data
Ejemplo n.º 31
0
def validate_ipv6_network(value):
    [addr, mask] = is_cidr_notation(value)
    mask_validator(mask, 'ipv6')
    validate_ipv6_address(addr)
Ejemplo n.º 32
0
def dynamic_ip_update_view(request):
    """
    
    TODO: explain dynamic IP update options and logic
    
    if hostname is missing, the ips of all A and AAAA records of the zone are changed
    otherwise only the specific record with the name=hostname and provided that the
    correct ip (v4, v6) has been provided for the type of the record (A, AAAA)
    
    If no ipv4 or ipv6 address is provided, then the client IP address is used
    to update A records (if the client IP is IPv4) or AAAA records (if client IP is IPv6).
    
    curl -k \
        -F "api_key=UBSE1RJ0J175MRAMJC31JFUH" \
        -F "hostname=ns1.centos.example.org" \
        -F "ipv4=10.1.2.3" \
        -F "ipv6=3ffe:1900:4545:3:200:f8ff:fe21:67cf" \
        https://centos.example.org/powerdns/update/

    """
    if request.method != 'POST':
        return HttpResponseNotAllowed(['POST'])
    form = DynamicIPUpdateForm(request.POST)

    if not form.is_valid():
        return HttpResponseBadRequest(repr(form.errors))

    # Determine protocol or REMOTE_ADDR
    remote_ipv4 = None
    remote_ipv6 = None
    try:
        validate_ipv4_address(request.META['REMOTE_ADDR'])
    except ValidationError:
        try:
            validate_ipv6_address(request.META['REMOTE_ADDR'])
        except ValidationError:
            return HttpResponseBadRequest(
                'Cannot determine protocol of remote IP address')
        else:
            remote_ipv6 = request.META['REMOTE_ADDR']
    else:
        remote_ipv4 = request.META['REMOTE_ADDR']

    # Gather required information

    # API key

    api_key = form.cleaned_data['api_key']

    # Hostname

    hostname = form.cleaned_data['hostname']

    # If the hostname is missing, the IP addresses of all A and AAAA records
    # of the zone are updated.
    update_all_hosts_in_zone = False
    if not hostname:
        update_all_hosts_in_zone = True

    # IP addresses

    ipv4 = form.cleaned_data['ipv4']
    ipv6 = form.cleaned_data['ipv6']

    # If IP information is missing, the remote client's IP address will be used.
    if not ipv4 and not ipv6:
        if remote_ipv4:
            ipv4 = remote_ipv4
        if remote_ipv6:
            ipv6 = remote_ipv6

    # All required data is good. Process the request.

    DynamicZone = get_model('powerdns_manager', 'DynamicZone')
    Record = get_model('powerdns_manager', 'Record')

    # Get the relevant dynamic zone instance
    dyn_zone = DynamicZone.objects.get(api_key__exact=api_key)

    # Get A and AAAA records
    dyn_rrs = Record.objects.filter(domain=dyn_zone.domain,
                                    type__in=('A', 'AAAA'))
    if not dyn_rrs:
        return HttpResponseNotFound('A or AAAA resource records not found')

    # Check existence of hostname
    if hostname:
        hostname_exists = False
        for rr in dyn_rrs:
            if rr.name == hostname:
                hostname_exists = True
                break
        if not hostname_exists:
            return HttpResponseNotFound('error:Hostname not found: %s' %
                                        hostname)

    # Update the IPs

    rr_has_changed = False

    if update_all_hosts_in_zone:  # No hostname supplied
        for rr in dyn_rrs:

            # Try to update A records
            if rr.type == 'A' and ipv4:
                rr.content = ipv4
                rr_has_changed = True

            # Try to update AAAA records
            elif rr.type == 'AAAA' and ipv6:
                rr.content = ipv6
                rr_has_changed = True

            rr.save()

    else:  # A hostname is supplied
        for rr in dyn_rrs:
            if rr.name == hostname:

                # Try to update A records
                if rr.type == 'A' and ipv4:
                    rr.content = ipv4
                    rr_has_changed = True

                # Try to update AAAA records
                elif rr.type == 'AAAA' and ipv6:
                    rr.content = ipv6
                    rr_has_changed = True

                rr.save()

    if rr_has_changed:
        return HttpResponse('Success')
    else:
        return HttpResponseNotFound('error:No suitable resource record found')
Ejemplo n.º 33
0
def dynamic_ip_update_view(request):
    """
    
    TODO: explain dynamic IP update options and logic
    
    if hostname is missing, the ips of all A and AAAA records of the zone are changed
    otherwise only the specific record with the name=hostname and provided that the
    correct ip (v4, v6) has been provided for the type of the record (A, AAAA)
    
    If no ipv4 or ipv6 address is provided, then the client IP address is used
    to update A records (if the client IP is IPv4) or AAAA records (if client IP is IPv6).
    
    curl -k \
        -F "api_key=UBSE1RJ0J175MRAMJC31JFUH" \
        -F "hostname=ns1.centos.example.org" \
        -F "ipv4=10.1.2.3" \
        -F "ipv6=3ffe:1900:4545:3:200:f8ff:fe21:67cf" \
        https://centos.example.org/powerdns/update/

    """
    if request.method != 'POST':
        return HttpResponseNotAllowed(['POST'])
    form = DynamicIPUpdateForm(request.POST)
    
    if not form.is_valid():
        return HttpResponseBadRequest(repr(form.errors))
    
    # Determine protocol or REMOTE_ADDR
    remote_ipv4 = None
    remote_ipv6 = None
    try:
        validate_ipv4_address(request.META['REMOTE_ADDR'])
    except ValidationError:
        try:
            validate_ipv6_address(request.META['REMOTE_ADDR'])
        except ValidationError:
            return HttpResponseBadRequest('Cannot determine protocol of remote IP address')
        else:
            remote_ipv6 = request.META['REMOTE_ADDR']
    else:
        remote_ipv4 = request.META['REMOTE_ADDR']
    
    # Gather required information
    
    # API key
    
    api_key = form.cleaned_data['api_key']
    
    # Hostname
    
    hostname = form.cleaned_data['hostname']
    
    # If the hostname is missing, the IP addresses of all A and AAAA records
    # of the zone are updated.
    update_all_hosts_in_zone = False
    if not hostname:
        update_all_hosts_in_zone = True
    
    # IP addresses
    
    ipv4 = form.cleaned_data['ipv4']
    ipv6 = form.cleaned_data['ipv6']

    # If IP information is missing, the remote client's IP address will be used.
    if not ipv4 and not ipv6:
        if remote_ipv4:
            ipv4 = remote_ipv4
        if remote_ipv6:
            ipv6 = remote_ipv6
    
    # All required data is good. Process the request.
    
    DynamicZone = cache.get_model('powerdns_manager', 'DynamicZone')
    Record = cache.get_model('powerdns_manager', 'Record')
    
    # Get the relevant dynamic zone instance
    dyn_zone = DynamicZone.objects.get(api_key__exact=api_key)
    
    # Get A and AAAA records
    dyn_rrs = Record.objects.filter(domain=dyn_zone.domain, type__in=('A', 'AAAA'))
    if not dyn_rrs:
        return HttpResponseNotFound('A or AAAA resource records not found')
    
    # Check existence of hostname
    if hostname:
        hostname_exists = False
        for rr in dyn_rrs:
            if rr.name == hostname:
                hostname_exists = True
                break
        if not hostname_exists:
            return HttpResponseNotFound('error:Hostname not found: %s' % hostname)
    
    # Update the IPs
    
    rr_has_changed = False
    
    if update_all_hosts_in_zone:    # No hostname supplied
        for rr in dyn_rrs:
            
            # Try to update A records
            if rr.type == 'A' and ipv4:
                rr.content = ipv4
                rr_has_changed = True
            
            # Try to update AAAA records
            elif rr.type == 'AAAA' and ipv6:
                rr.content = ipv6
                rr_has_changed = True
            
            rr.save()
        
    else:    # A hostname is supplied
        for rr in dyn_rrs:
            if rr.name == hostname:
                
                # Try to update A records
                if rr.type == 'A' and ipv4:
                    rr.content = ipv4
                    rr_has_changed = True
            
                # Try to update AAAA records
                elif rr.type == 'AAAA' and ipv6:
                    rr.content = ipv6
                    rr_has_changed = True
                
                rr.save()
    
    if rr_has_changed:
        return HttpResponse('Success')
    else:
        return HttpResponseNotFound('error:No suitable resource record found')
Ejemplo n.º 34
0
def add_object(type_,
               oid,
               object_type,
               name,
               source,
               method,
               reference,
               analyst,
               value=None,
               file_=None,
               add_indicator=False,
               get_objects=True,
               obj=None,
               is_sort_relationships=False,
               is_validate_only=False,
               is_validate_locally=False,
               cache={}):
    """
    Add an object to the database.

    :param type_: The top-level object type.
    :type type_: str
    :param oid: The ObjectId of the top-level object.
    :type oid: str
    :param object_type: The type of the ObjectType being added.
    :type object_type: str
    :param name: The name of the ObjectType being added.
    :type name: str
    :param source: The name of the source adding this object.
    :type source: str
    :param method: The method for this object.
    :type method: str
    :param reference: The reference for this object.
    :type reference: str
    :param analyst: The user adding this object.
    :type analyst: str
    :param value: The value of the object.
    :type value: str
    :param file_: The file if the object is a file upload.
    :type file_: file handle.
    :param add_indicator: Also add an indicator for this object.
    :type add_indicator: bool
    :param get_objects: Return the formatted list of objects when completed.
    :type get_object: bool
    :param obj: The CRITs top-level object we are adding objects to.
                This is an optional parameter used mainly for performance
                reasons (by not querying mongo if we already have the
                top level-object).
    :type obj: :class:`crits.core.crits_mongoengine.CritsBaseAttributes`
    :param is_validate_only: Only validate, do not add.
    :type is_validate_only: bool
    :param is_validate_locally: Only validate, do not add.
    :type is_validate_locally: bool
    :param cache: Cached data, typically for performance enhancements
                  during bulk operations.
    :type cache: dict
    :returns: dict with keys:
              "success" (boolean),
              "message" (str),
              "objects" (list),
              "relationships" (list)
    """

    results = {}

    if oid == None:
        oid = ""

    if obj == None:
        obj = class_from_id(type_, oid)

    if not obj:
        if is_validate_locally == True:
            # TODO: Perform some form of validation
            results['success'] = True
            return results
        else:
            results['message'] = "Could not find item to add object to."
            results['success'] = False
            return results

    if name == "URL" and "://" not in value.split('.')[0]:
        return {
            "success":
            False,
            "message":
            "URI - URL must contain protocol prefix (e.g. http://, https://, ftp://)"
        }
    elif object_type == "Address":
        if "ipv4" in name:
            try:
                validate_ipv4_address(value)
            except DjangoValidationError:
                return {"success": False, "message": "Invalid IPv4 address. "}
        elif "ipv6" in name:
            try:
                validate_ipv6_address(value)
            except DjangoValidationError:
                return {"success": False, "message": "Invalid IPv6 address. "}
        elif "cidr" in name:
            try:
                if '/' not in value:
                    raise ValidationError("")
                cidr_parts = value.split('/')
                if int(cidr_parts[1]) < 0 or int(cidr_parts[1]) > 128:
                    raise ValidationError("")
                if ':' not in cidr_parts[0] and int(cidr_parts[1]) > 32:
                    raise ValidationError("")
                validate_ipv46_address(cidr_parts[0])
            except (ValidationError, ValueError) as cidr_error:
                return {"success": False, "message": "Invalid CIDR address. "}

    try:
        cur_len = len(obj.obj)
        if file_:
            data = file_.read()
            filename = file_.name
            md5sum = md5(data).hexdigest()
            value = md5sum
            reference = filename
        obj.add_object(object_type, name, value, source, method, reference,
                       analyst)

        if is_validate_only == False:
            obj.save(username=analyst)

        new_len = len(obj.obj)
        if new_len > cur_len:
            results['message'] = "Object added successfully!"
            results['success'] = True
            if file_:
                # do we have a pcap?
                if data[:4] in ('\xa1\xb2\xc3\xd4', '\xd4\xc3\xb2\xa1',
                                '\x0a\x0d\x0d\x0a'):
                    handle_pcap_file(filename,
                                     data,
                                     source,
                                     user=analyst,
                                     related_id=oid,
                                     related_type=type_)
                else:
                    #XXX: MongoEngine provides no direct GridFS access so we
                    #     need to use pymongo directly.
                    col = settings.COL_OBJECTS
                    grid = mongo_connector("%s.files" % col)
                    if grid.find({'md5': md5sum}).count() == 0:
                        put_file(filename, data, collection=col)
            if add_indicator and is_validate_only == False:
                from crits.indicators.handlers import handle_indicator_ind

                if object_type != name:
                    object_type = "%s - %s" % (object_type, name)

                campaign = obj.campaign if hasattr(obj, 'campaign') else None
                ind_res = handle_indicator_ind(value,
                                               source,
                                               reference,
                                               object_type,
                                               analyst,
                                               method,
                                               add_domain=True,
                                               campaign=campaign,
                                               cache=cache)

                if ind_res['success']:
                    ind = ind_res['object']
                    forge_relationship(left_class=obj,
                                       right_class=ind,
                                       rel_type="Related_To",
                                       analyst=analyst,
                                       get_rels=is_sort_relationships)
                else:
                    results['message'] = "Object was added, but failed to add Indicator." \
                                         "<br>Error: " + ind_res.get('message')

            if is_sort_relationships == True:
                if file_ or add_indicator:
                    # does this line need to be here?
                    # obj.reload()
                    results['relationships'] = obj.sort_relationships(
                        analyst, meta=True)
                else:
                    results['relationships'] = obj.sort_relationships(
                        analyst, meta=True)

        else:
            results[
                'message'] = "Object already exists! [Type: " + object_type + "][Value: " + value + "] "
            results['success'] = False
        if (get_objects):
            results['objects'] = obj.sort_objects()

        results['id'] = str(obj.id)
        return results
    except ValidationError, e:
        return {'success': False, 'message': str(e)}
Ejemplo n.º 35
0
    def clean(self):
        '''Handles the validation logic for the domain record form.

        Validation gets pretty complicated due to the fact that the different
        record types (A, AAAA, MX, etc) have different requirements for
        each of the fields.
        '''

        cleaned_data = super(RecordForm, self).clean()
        record_type = cleaned_data['type']
        domain_name = cleaned_data['domain_name']
        if limit_records_to_fips():
            ip_addr = cleaned_data.pop('ip_addr')
            if (record_type in ['AAAA', 'A'] and limit_records_to_fips()):
                cleaned_data['data'] = ip_addr

        #  Name field
        if self._is_field_blank(cleaned_data, 'name'):
            if record_type in ['CNAME', 'SRV']:
                self._add_required_field_error('name')
            elif record_type in ['MX', 'A', 'AAAA', 'TXT', 'PTR']:
                cleaned_data['name'] = domain_name
        else:
            if record_type == 'SRV':
                if not re.match(SRV_NAME_REGEX, cleaned_data['name']):
                    self._add_field_error('name', _('Enter a valid SRV name'))
                else:
                    cleaned_data['name'] += domain_name
            else:
                cleaned_data['name'] += "." + domain_name
                if not re.match(WILDCARD_DOMAIN_NAME_REGEX,
                                cleaned_data['name']):
                    self._add_field_error('name',
                                          _('Enter a valid hostname. The '
                                            'hostname should contain letters '
                                            'and numbers, and be no more than '
                                            '63 characters.'))
        # Data field
        if self._is_field_blank(cleaned_data, 'data'):
            if record_type in ['A', 'AAAA', 'CNAME', 'MX', 'SRV']:
                self._add_required_field_error('data')
        else:
            if record_type == 'A':
                try:
                    validators.validate_ipv4_address(cleaned_data['data'])
                except ValidationError:
                    self._add_field_error('data',
                                          _('Enter a valid IPv4 address'))

            elif record_type == 'AAAA':
                try:
                    validators.validate_ipv6_address(cleaned_data['data'])
                except ValidationError:
                    self._add_field_error('data',
                                          _('Enter a valid IPv6 address'))

            elif record_type in ['CNAME', 'MX', 'PTR']:
                if not re.match(DOMAIN_NAME_REGEX, cleaned_data['data']):
                    self._add_field_error('data', _('Enter a valid hostname'))

            elif record_type == 'SRV':
                if not re.match(SRV_DATA_REGEX, cleaned_data['data']):
                    self._add_field_error('data',
                                          _('Enter a valid SRV record'))

        # Txt field
        if self._is_field_blank(cleaned_data, 'txt'):
            if record_type == 'TXT':
                self._add_required_field_error('txt')
        else:
            if record_type == 'TXT':
                cleaned_data['data'] = cleaned_data['txt']

        if record_type == 'SSHFP':
            if not re.match(SSHFP_DATA_REGEX, cleaned_data['txt']):
                self._add_field_error('txt',
                                      _('Enter a valid SSHFP record'))
            cleaned_data['data'] = cleaned_data['txt']

        cleaned_data.pop('txt')

        # Priority field
        # Check against '' instead of using _is_field_blank because we need to
        # allow a valud of 0.
        if ('priority' not in cleaned_data or
                cleaned_data['priority'] == '' or
                cleaned_data['priority'] is None):
            if record_type in ['MX', 'SRV']:
                self._add_required_field_error('priority')

        # Rename 'id' to 'record_id'
        if 'id' in cleaned_data:
            cleaned_data['record_id'] = cleaned_data.pop('id')

        # Remove domain_name
        cleaned_data.pop('domain_name')

        return cleaned_data