def get_config(): """ Get the CRIPTs configuration. :returns: :class:`cripts.config.config.CRIPTsConfig` """ cripts_config = CRIPTsConfig.objects().first() if not cripts_config: cripts_config = CRIPTsConfig() cripts_config.save() return cripts_config
def clean_db(): """ Clean up the DB after testing. """ src = SourceAccess.objects(name=TSRC).first() if src: src.delete() user = CRIPTsUser.objects(username=TUSER_NAME).first() if user: user.delete() TestObject.drop_collection() TestSourceObject.drop_collection() CRIPTsConfig.drop_collection()
def __init__(self, username=None, email=None): """ Set the default subject, body, from address, and other bits. """ # set the user and email address to send to self.username = username self.email = email # grab the CRIPTs url to use for links cripts_config = CRIPTsConfig.objects().first() self.instance_url = cripts_config.instance_url if self.instance_url.endswith("/"): self.instance_url = self.instance_url[:-1] # set the email address to send this email from self.from_address = cripts_config.cripts_email # setup the email subject if cripts_config.cripts_email_end_tag: self.subject = "CRIPTs: Subscriptions and Notifications" + cripts_config.cripts_email_subject_tag else: self.subject = cripts_config.cripts_email_subject_tag + "CRIPTs: Subscriptions and Notifications" # start the body of the email comments_url = self.instance_url + reverse('cripts.comments.views.activity') self.body = "Here's info on the latest comments and updates to CRIPTs that you are subscribed to!\n\n" self.body += "For more info, check out the Activity page: %s\n\n" % comments_url
def upgrade(lv, options): """ Perform the upgrade. :param lv: The CRIPTs version we are running. :type lv: str :param options: The options passed in for what to upgrade. :type options: dict """ # eventually we will do something to check to see what the current version # of the CRIPTs DB is so we can upgrade through several versions at once. # this is important if prep scripts need to be run for certain upgrades # to work properly. mall = options.get('mall') events = options.get('events') skip = options.get('skip') # run prep migrations if not skip: prep_database() # run full migrations if mall or events: migrate_collection(Event, sort_ids) # Always bump the version to the latest in settings.py config = CRIPTsConfig.objects() if len(config) > 1: print "You have more than one config object. This is really bad." else: config = config[0] config.cripts_version = settings.CRIPTS_VERSION config.save()
def reset_password(self, rcode, new_p, new_p_c, analyst): """ Reset the user's password. Validate the reset code, ensure the two passwords are identical, and then set. :param rcode: Reset Code to validate. :type rcode: str :param new_p: New password. :type new_p: str :param new_p_c: New password confirmation. :type new_p_c: str :param analyst: The user. :type analyst: str :returns: dict with keys "success" (boolean) and "message" (str). """ if self.validate_reset_code(rcode, analyst)['success']: if new_p == new_p_c: self.password_reset.reset_code = "" if self.set_password(new_p): return {'success': True, 'message': 'Password reset.'} else: cripts_config = CRIPTsConfig.objects().first() if cripts_config: pw_desc = cripts_config.password_complexity_desc else: pw_desc = settings.PASSWORD_COMPLEXITY_DESC message = 'Password not complex enough: %s' % pw_desc return {'success': False, 'message': message} else: return {'success': False, 'message': 'Passwords do not match.'} else: self.password_reset.reset_code = "" self.save(username=analyst) return {'success': False, 'message': 'Reset Code Expired.'}
def __init__(self, username=None, email=None): """ Set the default subject, body, from address, and other bits. """ # set the user and email address to send to self.username = username self.email = email # grab the CRIPTs url to use for links cripts_config = CRIPTsConfig.objects().first() self.instance_url = cripts_config.instance_url if self.instance_url.endswith("/"): self.instance_url = self.instance_url[:-1] # set the email address to send this email from self.from_address = cripts_config.cripts_email # setup the email subject if cripts_config.cripts_email_end_tag: self.subject = "CRIPTs: Subscriptions and Notifications" + cripts_config.cripts_email_subject_tag else: self.subject = cripts_config.cripts_email_subject_tag + "CRIPTs: Subscriptions and Notifications" # start the body of the email comments_url = self.instance_url + reverse( 'cripts.comments.views.activity') self.body = "Here's info on the latest comments and updates to CRIPTs that you are subscribed to!\n\n" self.body += "For more info, check out the Activity page: %s\n\n" % comments_url
def authenticate(self, username, password=None, user_agent=None, remote_addr=None, accept_language=None, totp_enabled='Disabled'): """ Perform the authentication of the user. :param username: The user to authenticate. :type username: str :param password: The password provided to authenticate with. :type password: str :param user_agent: The user-agent in the request. :type user_agent: str :param remote_addr: The hostname/ip in the request. :type remote_addr: str :param accept_language: The Accept Language in the request. :type accept_language: str :param totp_enabled: If TOTP is enabled and should be checked as well. :type totp_enabled: str :returns: :class:`cripts.core.user.CRIPTsUser`, None """ e = EmbeddedLoginAttempt() e.user_agent = user_agent e.remote_addr = remote_addr e.accept_language = accept_language if not username: logger.warn("No username passed to CRIPTsRemoteUserBackend (auth)") return None config = CRIPTsConfig.objects().first() user = None username = self.clean_username(username) user = CRIPTsUser.objects(username=username).first() if user and user.is_active: if self._exceeded_login_threshold(user): return None # Log in user self._successful_settings(user, e, totp_enabled) if config.ldap_update_on_login: user.update_from_ldap("Auto LDAP update", config) return user elif not user and config.create_unknown_user: # Create the user user = CRIPTsUser.create_user(username=username, password=None) user.sources.append(config.company_name) # Attempt to update info from LDAP user.update_from_ldap("Auto LDAP update", config) user = self._successful_settings(user, e, totp_enabled) return user else: logger.warn("Unknown user and not creating accounts.") return None
def create_config_if_not_exist(): """ If the CRIPTsConfig already exists then the CRIPTsConfig is returned, otherwise a new CRIPTsConfig will be created, saved, and returned. Returns: Returns the CRIPTsConfig if it already exists, otherwise a default CRIPTsConfig is returned. """ cripts_config = CRIPTsConfig.objects().first() if not cripts_config: print "Creating a new CRIPTs configuration." cripts_config = CRIPTsConfig() cripts_config.save() else: print "A CRPTs configuration already exists. Skipping default creation." return cripts_config
def email_user(self, subject, message, from_email=None): """ Sends an e-mail to this User. """ from django.core.mail import send_mail if not from_email: cripts_config = CRIPTsConfig.objects().first() if cripts_config: from_email = cripts_config.cripts_email send_mail(subject, message, from_email, [self.email])
def prep_db(): """ Prep the DB for testing. """ clean_db() # Create a new default config cripts_config = CRIPTsConfig() cripts_config.save() # Add Source handlers.add_new_source(TSRC, TRANDUSER) # Add User user = CRIPTsUser.create_user( username=TUSER_NAME, password=TUSER_PASS, email=TUSER_EMAIL, ) user.first_name = TUSER_FNAME user.last_name = TUSER_LNAME user.save() # Add test source object obj = TestSourceObject() obj.name = TOBJS_NAME obj.value = TOBJS_VALUE obj.add_source(source=TSRC, analyst=TUSER_NAME) obj.save() # Add another with Different source obj = TestSourceObject() obj.name = TOBJS_NAME obj.value = TOBJS_VALUE obj.add_source(source=TUNKSRC, analyst=TRANDUSER) obj.save() # Add test non-source object obj = TestObject() obj.name = TOBJ_NAME obj.value = TOBJ_VALUE obj.save()
def prep_db(): """ Prep the DB for testing. """ clean_db() # Create a new default config cripts_config = CRIPTsConfig() cripts_config.save() # Add Source handlers.add_new_source(TSRC, TRANDUSER) # Add User user = CRIPTsUser.create_user(username=TUSER_NAME, password=TUSER_PASS, email=TUSER_EMAIL, ) user.first_name = TUSER_FNAME user.last_name = TUSER_LNAME user.save() # Add test source object obj = TestSourceObject() obj.name = TOBJS_NAME obj.value = TOBJS_VALUE obj.add_source(source=TSRC, analyst=TUSER_NAME) obj.save() # Add another with Different source obj = TestSourceObject() obj.name = TOBJS_NAME obj.value = TOBJS_VALUE obj.add_source(source=TUNKSRC, analyst=TRANDUSER) obj.save() # Add test non-source object obj = TestObject() obj.name = TOBJ_NAME obj.value = TOBJ_VALUE obj.save()
def __init__(self, *args, **kwargs): cripts_config = CRIPTsConfig.objects().first() depth_max = getattr(cripts_config, 'depth_max', settings.DEPTH_MAX) total_max = getattr(cripts_config, 'total_max', settings.TOTAL_MAX) rel_max = getattr(cripts_config, 'rel_max', settings.REL_MAX) super(DownloadFileForm, self).__init__(*args, **kwargs) self.fields['objects'].choices = [('Dataset', 'Datasets'), ('EmailAddress', 'EmailAddresses'), ('Hash', 'Hashes'), ('Target', 'Targets'), ('UserName', 'UserNames')] self.fields['total_limit'].initial = total_max self.fields['rel_limit'].initial = rel_max self.fields['depth_limit'].help_text = self.fields['depth_limit'].help_text % depth_max self.fields['total_limit'].help_text = self.fields['total_limit'].help_text % total_max self.fields['rel_limit'].help_text = self.fields['rel_limit'].help_text % rel_max
def force_reset_config(): """ Resets the values for the CRIPTsConfig class by dropping the database collection and then saving a new default CRIPTsConfig. """ print "Resetting CRIPTs configuration settings." CRIPTsConfig.drop_collection(); cripts_config = CRIPTsConfig(); cripts_config.save();
def is_password_complex(self, password): """ Based on the CRIPTsConfig, is the password provided complex enough to be used? :param password: The password to check for complexity. :type password: str :returns: True, False """ cripts_config = CRIPTsConfig.objects().first() if cripts_config: pw_regex = cripts_config.password_complexity_regex else: pw_regex = settings.PASSWORD_COMPLEXITY_REGEX complex_regex = re.compile(pw_regex) if complex_regex.match(password): return True return False
def __init__(self, *args, **kwargs): cripts_config = CRIPTsConfig.objects().first() depth_max = getattr(cripts_config, 'depth_max', settings.DEPTH_MAX) total_max = getattr(cripts_config, 'total_max', settings.TOTAL_MAX) rel_max = getattr(cripts_config, 'rel_max', settings.REL_MAX) super(DownloadFileForm, self).__init__(*args, **kwargs) self.fields['objects'].choices = [('Dataset', 'Datasets'), ('EmailAddress', 'EmailAddresses'), ('Hash', 'Hashes'), ('Target', 'Targets'), ('UserName', 'UserNames')] self.fields['total_limit'].initial = total_max self.fields['rel_limit'].initial = rel_max self.fields['depth_limit'].help_text = self.fields[ 'depth_limit'].help_text % depth_max self.fields['total_limit'].help_text = self.fields[ 'total_limit'].help_text % total_max self.fields['rel_limit'].help_text = self.fields[ 'rel_limit'].help_text % rel_max
def temp_password(self): """ Temporary password must match the password complexity regex. If we don't have one in the DB use the one out of settings. """ cripts_config = CRIPTsConfig.objects().first() if cripts_config: pw_regex = cripts_config.password_complexity_regex else: pw_regex = settings.PASSWORD_COMPLEXITY_REGEX rex = re.compile(pw_regex) chars = string.letters + string.digits + string.punctuation for i in xrange(20): passwd = '' while len(passwd) < 50: passwd += choice(chars) if rex.match(passwd): return passwd raise CommandError("Unable to generate complex enough password.")
def cripts_config(request): """ Generate the CRIPTs Configuration template. :param request: Django request object (Required) :type request: :class:`django.http.HttpRequest` :returns: :class:`django.http.HttpResponse` """ cripts_config = CRIPTsConfig.objects().first() if cripts_config: cripts_config = cripts_config.to_dict() cripts_config['allowed_hosts'] = ", ".join( cripts_config['allowed_hosts']) cripts_config['service_dirs'] = ", ".join( cripts_config['service_dirs']) config_general_form = ConfigGeneralForm(initial=cripts_config) config_LDAP_form = ConfigLDAPForm(initial=cripts_config) config_security_form = ConfigSecurityForm(initial=cripts_config) config_logging_form = ConfigLoggingForm(initial=cripts_config) config_services_form = ConfigServicesForm(initial=cripts_config) config_download_form = ConfigDownloadForm(initial=cripts_config) config_CRIPTs_form = ConfigCriptsForm(initial=cripts_config) else: config_general_form = ConfigGeneralForm() config_LDAP_form = ConfigLDAPForm() config_security_form = ConfigSecurityForm() config_logging_form = ConfigLoggingForm() config_services_form = ConfigServicesForm() config_download_form = ConfigDownloadForm() config_CRIPTs_form = ConfigCriptsForm() return render_to_response( 'config.html', { 'config_general_form': config_general_form, 'config_LDAP_form': config_LDAP_form, 'config_security_form': config_security_form, 'config_logging_form': config_logging_form, 'config_services_form': config_services_form, 'config_download_form': config_download_form, 'config_CRIPTs_form': config_CRIPTs_form }, RequestContext(request))
def change_user_password(username, current_p, new_p, new_p_c): """ Change the password for a user. :param username: The user to query for. :type username: str :param current_p: The user's current password. :type current_p: str :param new_p: The new password. :type new_p: str :param new_p_c: New password confirmation. :type new_p_c: str :returns: dict with keys "success" (boolean) and "message" (str) if failed. """ if new_p != new_p_c: return { 'success': False, 'message': 'New password confirmation does not match.' } from cripts.core.user import CRIPTsUser username = str(username) user = CRIPTsUser.objects(username=username).first() if not user: return {'success': False, 'message': 'Unknown user.'} if not user.check_password(current_p): return {'success': False, 'message': 'Current password invalid.'} if user.set_password(new_p, username): return {'success': True, 'message': 'Password Change Successful.'} else: from cripts.config.config import CRIPTsConfig cripts_config = CRIPTsConfig.objects().first() if cripts_config: regex_desc = cripts_config.password_complexity_desc else: regex_desc = settings.PASSWORD_COMPLEXITY_DESC return { 'success': False, 'message': 'Password not complex enough: %s' % regex_desc }
def cripts_config(request): """ Generate the CRIPTs Configuration template. :param request: Django request object (Required) :type request: :class:`django.http.HttpRequest` :returns: :class:`django.http.HttpResponse` """ cripts_config = CRIPTsConfig.objects().first() if cripts_config: cripts_config = cripts_config.to_dict() cripts_config['allowed_hosts'] = ", ".join(cripts_config['allowed_hosts']) cripts_config['service_dirs'] = ", ".join(cripts_config['service_dirs']) config_general_form = ConfigGeneralForm(initial=cripts_config) config_LDAP_form = ConfigLDAPForm(initial=cripts_config) config_security_form = ConfigSecurityForm(initial=cripts_config) config_logging_form = ConfigLoggingForm(initial=cripts_config) config_services_form = ConfigServicesForm(initial=cripts_config) config_download_form = ConfigDownloadForm(initial=cripts_config) config_CRIPTs_form = ConfigCriptsForm(initial=cripts_config) else: config_general_form = ConfigGeneralForm() config_LDAP_form = ConfigLDAPForm() config_security_form = ConfigSecurityForm() config_logging_form = ConfigLoggingForm() config_services_form = ConfigServicesForm() config_download_form = ConfigDownloadForm() config_CRIPTs_form = ConfigCriptsForm() return render_to_response('config.html', {'config_general_form': config_general_form, 'config_LDAP_form': config_LDAP_form, 'config_security_form': config_security_form, 'config_logging_form': config_logging_form, 'config_services_form': config_services_form, 'config_download_form': config_download_form, 'config_CRIPTs_form': config_CRIPTs_form}, RequestContext(request))
def modify_configuration(forms, analyst): """ Modify the configuration with the submitted changes. :param config_form: The form data. :type config_form: dict :param analyst: The user making the modifications. :type analyst: str :returns: dict with key "message" """ config = CRIPTsConfig.objects().first() if not config: config = CRIPTsConfig() data = None for form in forms: if not data: data = form.cleaned_data else: data.update(form.cleaned_data) # data = config_form.cleaned_data allowed_hosts_list = data['allowed_hosts'].split(',') allowed_hosts = () for allowed_host in allowed_hosts_list: allowed_hosts = allowed_hosts + (allowed_host.strip(), ) data['allowed_hosts'] = allowed_hosts service_dirs_list = data['service_dirs'].split(',') service_dirs = () for service_dir in service_dirs_list: service_dirs = service_dirs + (service_dir.strip(), ) data['service_dirs'] = service_dirs config.merge(data, overwrite=True) try: config.save(username=analyst) return {'message': "Success!"} except Exception, e: return {'message': "Failure: %s" % e}
def change_user_password(username, current_p, new_p, new_p_c): """ Change the password for a user. :param username: The user to query for. :type username: str :param current_p: The user's current password. :type current_p: str :param new_p: The new password. :type new_p: str :param new_p_c: New password confirmation. :type new_p_c: str :returns: dict with keys "success" (boolean) and "message" (str) if failed. """ if new_p != new_p_c: return {"success": False, "message": "New password confirmation does not match."} from cripts.core.user import CRIPTsUser username = str(username) user = CRIPTsUser.objects(username=username).first() if not user: return {"success": False, "message": "Unknown user."} if not user.check_password(current_p): return {"success": False, "message": "Current password invalid."} if user.set_password(new_p, username): return {"success": True, "message": "Password Change Successful."} else: from cripts.config.config import CRIPTsConfig cripts_config = CRIPTsConfig.objects().first() if cripts_config: regex_desc = cripts_config.password_complexity_desc else: regex_desc = settings.PASSWORD_COMPLEXITY_DESC return {"success": False, "message": "Password not complex enough: %s" % regex_desc}
def update_database_version(): c = CRIPTsConfig.objects().first() c.cripts_version = "0.1.0" c.save()
def create_zip(files, pw_protect=True): """ Create a zip file. Creates a temporary directory to write files to on disk using :class:`tempfile`. Uses /usr/bin/zip as the zipping mechanism currently. Will password protect the zip file as a default. The password for the zip file defaults to "infected", but it can be changed in the config under zip7_password. :param files: The files to add to the zip file. :type files: list of files which are in the format of a list or tuple of (<filename>, <data>). :param pw_protect: To password protect the zip file or not. :type pw_protect: boolean :returns: :class:`cripts.core.exceptions.ZipFileError`, str """ dumpdir = "" try: # Zip can take data from stdin to compress, but # you can't define the filenames within the archive, # they show up as "-". Therefore, we need to write # out the file, compress it and return the zip. # Save the sample as a file in a temp directory # NOTE: the following line was causing a "permission denied" exception. # Removed dir arg. from cripts.config.config import CRIPTsConfig cripts_config = CRIPTsConfig.objects().first() if cripts_config: zip7_password = cripts_config.zip7_password or 'hashed' else: zip7_password = settings.ZIP7_PASSWORD or 'hashed' dumpdir = tempfile.mkdtemp() #dir=temproot #write out binary files for f in files: filename = f[0] file_data = f[1] # make sure our desired path doesn't already exist (some files may # have the same name but different data) path = dumpdir + "/" + filename.encode("utf-8") i = 1 tmp = path while os.path.exists(tmp): tmp = path + "(" + str(i) + ")" i += 1 with open(tmp, "wb") as fh: fh.write(file_data) # Build the command line for zip # NOTE: forking subprocess instead of using Python's ZipFile library # because ZipFile does not allow us to create password-protected zip # archives, only read them. # -j don't include original filepath zipname = "zip.zip" #The name we give it doesn't really matter args = ["/usr/bin/zip", "-r", "-j", dumpdir + "/" + zipname, dumpdir] if pw_protect: args += ["-P", zip7_password] args += [dumpdir + "/" + zipname, dumpdir] proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # Give the process 30 seconds to complete, otherwise kill it waitSeconds = 30 while (proc.poll() is None and waitSeconds): time.sleep(1) waitSeconds -= 1 zipdata = "" if proc.returncode: # zip spit out an error errmsg = "Error while creating archive\n" + proc.stdout.read() raise ZipFileError, errmsg elif not waitSeconds: # Process timed out proc.terminate() raise ZipFileError, "Error:\nProcess failed to terminate" else: with open(dumpdir + "/" + zipname, "rb") as fh: zipdata = fh.read() if not len(zipdata): raise ZipFileError, "Error:\nThe zip archive contains no data" return zipdata except ZipFileError: raise except Exception, ex: errmsg = "" for err in ex.args: errmsg = errmsg + " " + unicode(err) raise ZipFileError, errmsg
class Command(BaseCommand): """ Script Class. """ option_list = BaseCommand.option_list + ( make_option('--adduser', '-a', dest='adduser', action='store_true', default=False, help='Add a new user to CRIPTs.'), make_option('--administrator', '-A', dest='admin', action='store_true', default=False, help='Make this user an administrator.'), make_option('--clearsecret', '-c', dest='clearsecret', action='store_true', default=False, help="Clear a user's secret."), make_option('--deactivateuser', '-d', dest='deactivate', action='store_true', default=False, help='Deactivate a user account.'), make_option('--email', '-e', dest='email', default=None, help='Email address of new user.'), make_option('--sendemail', '-E', dest='sendemail', action='store_true', default=False, help='Email new user their temporary password.'), make_option('--firstname', '-f', dest='firstname', default='', help='First name of new user.'), make_option('--invalidreset', '-i', dest='invalidreset', action='store_true', default=False, help="Reset a user's invalid login attempts to 0."), make_option('--lastname', '-l', dest='lastname', default='', help='Last name of new user.'), make_option('--organization', '-o', dest='organization', default='', help='Assign user to an organization/source.'), make_option('--reset', '-r', dest='reset', action='store_true', default=False, help='Assign a new temporary password to a user.'), make_option('--setactive', '-s', dest='setactive', action='store_true', default=False, help='Set a user account to active.'), make_option('--enabletotp', '-t', dest='enabletotp', action='store_true', default=False, help='Enable TOTP for a user.'), make_option('--disabletotp', '-T', dest='disabletotp', action='store_true', default=False, help='Disable TOTP for a user.'), make_option('--username', '-u', dest='username', default=None, help='Username for new user.'), ) help = 'Add and edit a CRIPTs user. If "-a" is not used, we will try to edit.' def handle(self, *args, **options): """ Script execution. """ adduser = options.get('adduser') admin = options.get('admin') clearsecret = options.get('clearsecret') deactivate = options.get('deactivate') disabletotp = options.get('disabletotp') email = options.get('email') enabletotp = options.get('enabletotp') firstname = options.get('firstname') invalidreset = options.get('invalidreset') lastname = options.get('lastname') sendemail = options.get('sendemail') organization = options.get('organization') password = self.temp_password() reset = options.get('reset') setactive = options.get('setactive') username = options.get('username') # We always need a username if not username: raise CommandError("Must provide a username.") user = CRIPTsUser.objects(username=username).first() # If we've found a user with that username and we aren't trying to add a # new user... if user and not adduser: if admin: user.role = "Administrator" if clearsecret: user.secret = "" if deactivate and not setactive: user.is_active = False if disabletotp and not enabletotp: user.totp = False if email: user.email = email if enabletotp and not disabletotp: user.totp = True if firstname: user.first_name = firstname if lastname: user.last_name = lastname if invalidreset: user.invalid_login_attempts = 0 if organization: user.organization = organization if reset: user.set_password(password) if setactive and not deactivate: user.is_active = True try: user.save() if reset: print "New temporary password for %s: %s" % (username, password) print "User %s has been updated!" % username except Exception, e: raise CommandError("Error saving changes: %s" % str(e)) if adduser: raise CommandError("User '%s' exists in CRIPTs!" % username) elif adduser: if not email: raise CommandError("Must provide an email address!") user = CRIPTsUser.create_user(username, password, email) user.first_name = firstname user.last_name = lastname user.is_staff = True user.save() user.organization = organization if admin: user.role = "Administrator" user.save() if sendemail: cripts_config = CRIPTsConfig.objects().first() if cripts_config.cripts_email_end_tag: subject = "New CRIPTs User Account" + cripts_config.cripts_email_subject_tag else: subject = cripts_config.cripts_email_subject_tag + "New CRIPTs User Account" body = """You are receiving this email because someone has created a CRIPTs account for you. If you feel like you have received this in error, please ignore this email. Your account information is below:\n\n """ body += "Username:\t%s\n" % username body += "Password:\t%s\n\n\n" % password body += """You should log in immediately and reset your password.\n Thank you! """ user.email_user(subject, body) self.stdout.write("User '%s' created successfully!" % username) self.stdout.write("\nTemp password: \t%s" % password) self.stdout.write("\n")
def authenticate(self, username=None, password=None, user_agent=None, remote_addr=None, accept_language=None, totp_enabled='Disabled'): """ Perform the authentication of the user. :param username: The user to authenticate. :type username: str :param password: The password provided to authenticate with. :type password: str :param user_agent: The user-agent in the request. :type user_agent: str :param remote_addr: The hostname/ip in the request. :type remote_addr: str :param accept_language: The Accept Language in the request. :type accept_language: str :param totp_enabled: If TOTP is enabled and should be checked as well. :type totp_enabled: str :returns: :class:`cripts.core.user.CRIPTsUser`, None """ # Need username and password for logins, checkem both if not all([username, password]): return None e = EmbeddedLoginAttempt() e.user_agent = user_agent e.remote_addr = remote_addr e.accept_language = accept_language fusername = username if '\\' in username: username = username.split("\\")[1] user = CRIPTsUser.objects(username=username).first() if user: # If the user needs TOTP and it is not disabled system-wide, and # the user has exceeded the login threshold for this time period # don't go any further. Track the invalid login and return. if (((user.totp and totp_enabled == 'Optional') or totp_enabled == 'Required') and self._exceeded_login_threshold(user)): e.success = False self.track_login_attempt(user, e) user.reload() return None config = CRIPTsConfig.objects().first() if not config: return None if config.ldap_auth: import ldap, ldapurl try: # If you are using Oracle's server that's based on # Netscape's code, and your users can't login after # password expiration warning kicks in, you need: # python-ldap 2.4.15 installed and # import ldap.controls.pwdpolicy to fix it # import ldap.controls.pwdpolicy except ImportError: logger.info("ldap.controls.pwdpolicy not present.") try: # don't parse the port if there is one ldap_server = config.ldap_server.split(':') scheme = "ldap" if config.ldap_tls: scheme = "ldaps" url = ldapurl.LDAPUrl('%s://%s' % (scheme, ldap_server[0])) if len(ldap_server) == 2: l = ldap.initialize('%s:%s' % (url.unparse(), ldap_server[1])) else: l = ldap.initialize(url.unparse()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_TIMEOUT, 10) # two-step ldap binding if len(config.ldap_bind_dn) > 0: try: logger.info("binding with bind_dn: %s" % config.ldap_bind_dn) l.simple_bind_s(config.ldap_bind_dn, config.ldap_bind_password) filter = '(|(cn='+fusername+')(uid='+fusername+')(mail='+fusername+'))' # use the retrieved dn for the second bind un = l.search_s(config.ldap_userdn,ldap.SCOPE_SUBTREE,filter,['dn'])[0][0] except Exception as err: #logger.error("Error binding to LDAP for: %s" % config.ldap_bind_dn) logger.error("authenticate ERR: %s" % err) l.unbind() if len(ldap_server) == 2: l = ldap.initialize('%s:%s' % (url.unparse(), ldap_server[1])) else: l = ldap.initialize(url.unparse()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_TIMEOUT, 10) else: un = fusername # setup auth for custom cn's if len(config.ldap_usercn) > 0: un = "%s%s,%s" % (config.ldap_usercn, fusername, config.ldap_userdn) elif "@" in config.ldap_userdn: un = "%s%s" % (fusername, config.ldap_userdn) logger.info("Logging in user: %s" % un) l.simple_bind_s(un, password) user = self._successful_settings(user, e, totp_enabled) if config.ldap_update_on_login: user.update_from_ldap("Auto LDAP update", config, password) l.unbind() return user except ldap.INVALID_CREDENTIALS: l.unbind() logger.info("Invalid LDAP credentials for: %s" % un) except Exception as err: logger.info("LDAP Auth error: %s" % err) # If LDAP auth fails, attempt normal CRIPTs auth. # This will help with being able to use local admin accounts when # you have LDAP auth enabled. if password and user.check_password(password): self._successful_settings(user, e, totp_enabled) if config.ldap_update_on_login: user.update_from_ldap("Auto LDAP update", config) return user else: e.success = False user.invalid_login_attempts += 1 if user.is_active and user.invalid_login_attempts > settings.INVALID_LOGIN_ATTEMPTS: user.is_active = False logger.info("Account disabled due to too many invalid login attempts: %s" % user.username) if config.cripts_email_end_tag: subject = "CRIPTs Account Lockout" + config.cripts_email_subject_tag else: subject = config.cripts_email_subject_tag + "CRIPTs Account Lockout" body = """ You are receiving this email because your CRIPTs account has been locked out due to too many invalid login attempts. If you did not perform this action, someone may be attempting to access your account. Please contact a site administrator to resolve. """ try: user.email_user(subject, body) except Exception, err: logger.warning("Error sending email: %s" % str(err)) self.track_login_attempt(user, e) user.reload()
def info_from_ldap(self, config=None, password=''): """ Get information about this user from LDAP. """ import ldap, ldapurl resp = {"result": "ERROR"} if not config: config = CRIPTsConfig.objects().first() # Make sure we have the rquired settings, else return failure if not config.ldap_server or not config.ldap_userdn: return resp ldap_server = config.ldap_server.split(':') scheme = "ldap" if config.ldap_tls: scheme = "ldaps" url = ldapurl.LDAPUrl('%s://%s' % (scheme, ldap_server[0])) if len(ldap_server) == 2: l = ldap.initialize('%s:%s' % (url.unparse(), ldap_server[1])) else: l = ldap.initialize(url.unparse()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_TIMEOUT, 10) # setup auth for custom cn's cn = "cn=" if config.ldap_usercn: cn = config.ldap_usercn # two-step ldap binding if len(config.ldap_bind_dn) > 0: try: logger.info("binding with bind_dn: %s" % config.ldap_bind_dn) l.simple_bind_s(config.ldap_bind_dn, config.ldap_bind_password) filter = '(|(cn='+self.username+')(uid='+self.username+')(mail='+self.username+'))' # use the retrieved dn for the second bind un = l.search_s(config.ldap_userdn,ldap.SCOPE_SUBTREE,filter,['dn'])[0][0] except Exception as err: #logger.error("Error binding to LDAP for: %s" % config.ldap_bind_dn) logger.error("Error in info_from_ldap: %s" % err) l.unbind() if len(ldap_server) == 2: l = ldap.initialize('%s:%s' % (url.unparse(), ldap_server[1])) else: l = ldap.initialize(url.unparse()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_TIMEOUT, 10) else: un = self.username # setup auth for custom cn's if len(config.ldap_usercn) > 0: un = "%s%s,%s" % (config.ldap_usercn, self.username, config.ldap_userdn) elif "@" in config.ldap_userdn: un = "%s%s" % (self.username, config.ldap_userdn) try: # Try auth bind first l.simple_bind_s(un, password) logger.info("Bound to LDAP for: %s" % un) except Exception as e: #logger.error("Error binding to LDAP for: %s" % self.username) logger.error("info_from_ldap:ERR: %s" % e) try: uatr = None uatr = l.search_s(config.ldap_userdn, ldap.SCOPE_SUBTREE, '(|(cn='+self.username+')(uid='+self.username+'))' )[0][1] resp['first_name'] = uatr['givenName'][0] resp['last_name'] = uatr['sn'][0] resp['email'] = uatr['mail'][0] resp['result'] = "OK" logger.info("Retrieved LDAP info for: %s" % self.username) except Exception as e: #logger.error("Error retrieving LDAP info for: %s" % self.username) logger.error("info_from_ldap ERR: %s" % e) l.unbind() return resp
def info_from_ldap(self, config=None, password=''): """ Get information about this user from LDAP. """ import ldap, ldapurl resp = {"result": "ERROR"} if not config: config = CRIPTsConfig.objects().first() # Make sure we have the rquired settings, else return failure if not config.ldap_server or not config.ldap_userdn: return resp ldap_server = config.ldap_server.split(':') scheme = "ldap" if config.ldap_tls: scheme = "ldaps" url = ldapurl.LDAPUrl('%s://%s' % (scheme, ldap_server[0])) if len(ldap_server) == 2: l = ldap.initialize('%s:%s' % (url.unparse(), ldap_server[1])) else: l = ldap.initialize(url.unparse()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_TIMEOUT, 10) # setup auth for custom cn's cn = "cn=" if config.ldap_usercn: cn = config.ldap_usercn # two-step ldap binding if len(config.ldap_bind_dn) > 0: try: logger.info("binding with bind_dn: %s" % config.ldap_bind_dn) l.simple_bind_s(config.ldap_bind_dn, config.ldap_bind_password) filter = '(|(cn=' + self.username + ')(uid=' + self.username + ')(mail=' + self.username + '))' # use the retrieved dn for the second bind un = l.search_s(config.ldap_userdn, ldap.SCOPE_SUBTREE, filter, ['dn'])[0][0] except Exception as err: #logger.error("Error binding to LDAP for: %s" % config.ldap_bind_dn) logger.error("Error in info_from_ldap: %s" % err) l.unbind() if len(ldap_server) == 2: l = ldap.initialize('%s:%s' % (url.unparse(), ldap_server[1])) else: l = ldap.initialize(url.unparse()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_TIMEOUT, 10) else: un = self.username # setup auth for custom cn's if len(config.ldap_usercn) > 0: un = "%s%s,%s" % (config.ldap_usercn, self.username, config.ldap_userdn) elif "@" in config.ldap_userdn: un = "%s%s" % (self.username, config.ldap_userdn) try: # Try auth bind first l.simple_bind_s(un, password) logger.info("Bound to LDAP for: %s" % un) except Exception as e: #logger.error("Error binding to LDAP for: %s" % self.username) logger.error("info_from_ldap:ERR: %s" % e) try: uatr = None uatr = l.search_s( config.ldap_userdn, ldap.SCOPE_SUBTREE, '(|(cn=' + self.username + ')(uid=' + self.username + '))')[0][1] resp['first_name'] = uatr['givenName'][0] resp['last_name'] = uatr['sn'][0] resp['email'] = uatr['mail'][0] resp['result'] = "OK" logger.info("Retrieved LDAP info for: %s" % self.username) except Exception as e: #logger.error("Error retrieving LDAP info for: %s" % self.username) logger.error("info_from_ldap ERR: %s" % e) l.unbind() return resp
def authenticate(self, username=None, password=None, user_agent=None, remote_addr=None, accept_language=None, totp_enabled='Disabled'): """ Perform the authentication of the user. :param username: The user to authenticate. :type username: str :param password: The password provided to authenticate with. :type password: str :param user_agent: The user-agent in the request. :type user_agent: str :param remote_addr: The hostname/ip in the request. :type remote_addr: str :param accept_language: The Accept Language in the request. :type accept_language: str :param totp_enabled: If TOTP is enabled and should be checked as well. :type totp_enabled: str :returns: :class:`cripts.core.user.CRIPTsUser`, None """ # Need username and password for logins, checkem both if not all([username, password]): return None e = EmbeddedLoginAttempt() e.user_agent = user_agent e.remote_addr = remote_addr e.accept_language = accept_language fusername = username if '\\' in username: username = username.split("\\")[1] user = CRIPTsUser.objects(username=username).first() if user: # If the user needs TOTP and it is not disabled system-wide, and # the user has exceeded the login threshold for this time period # don't go any further. Track the invalid login and return. if (((user.totp and totp_enabled == 'Optional') or totp_enabled == 'Required') and self._exceeded_login_threshold(user)): e.success = False self.track_login_attempt(user, e) user.reload() return None config = CRIPTsConfig.objects().first() if not config: return None if config.ldap_auth: import ldap, ldapurl try: # If you are using Oracle's server that's based on # Netscape's code, and your users can't login after # password expiration warning kicks in, you need: # python-ldap 2.4.15 installed and # import ldap.controls.pwdpolicy to fix it # import ldap.controls.pwdpolicy except ImportError: logger.info("ldap.controls.pwdpolicy not present.") try: # don't parse the port if there is one ldap_server = config.ldap_server.split(':') scheme = "ldap" if config.ldap_tls: scheme = "ldaps" url = ldapurl.LDAPUrl('%s://%s' % (scheme, ldap_server[0])) if len(ldap_server) == 2: l = ldap.initialize('%s:%s' % (url.unparse(), ldap_server[1])) else: l = ldap.initialize(url.unparse()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_TIMEOUT, 10) # two-step ldap binding if len(config.ldap_bind_dn) > 0: try: logger.info("binding with bind_dn: %s" % config.ldap_bind_dn) l.simple_bind_s(config.ldap_bind_dn, config.ldap_bind_password) filter = '(|(cn=' + fusername + ')(uid=' + fusername + ')(mail=' + fusername + '))' # use the retrieved dn for the second bind un = l.search_s(config.ldap_userdn, ldap.SCOPE_SUBTREE, filter, ['dn'])[0][0] except Exception as err: #logger.error("Error binding to LDAP for: %s" % config.ldap_bind_dn) logger.error("authenticate ERR: %s" % err) l.unbind() if len(ldap_server) == 2: l = ldap.initialize( '%s:%s' % (url.unparse(), ldap_server[1])) else: l = ldap.initialize(url.unparse()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_TIMEOUT, 10) else: un = fusername # setup auth for custom cn's if len(config.ldap_usercn) > 0: un = "%s%s,%s" % (config.ldap_usercn, fusername, config.ldap_userdn) elif "@" in config.ldap_userdn: un = "%s%s" % (fusername, config.ldap_userdn) logger.info("Logging in user: %s" % un) l.simple_bind_s(un, password) user = self._successful_settings(user, e, totp_enabled) if config.ldap_update_on_login: user.update_from_ldap("Auto LDAP update", config, password) l.unbind() return user except ldap.INVALID_CREDENTIALS: l.unbind() logger.info("Invalid LDAP credentials for: %s" % un) except Exception as err: logger.info("LDAP Auth error: %s" % err) # If LDAP auth fails, attempt normal CRIPTs auth. # This will help with being able to use local admin accounts when # you have LDAP auth enabled. if password and user.check_password(password): self._successful_settings(user, e, totp_enabled) if config.ldap_update_on_login: user.update_from_ldap("Auto LDAP update", config) return user else: e.success = False user.invalid_login_attempts += 1 if user.is_active and user.invalid_login_attempts > settings.INVALID_LOGIN_ATTEMPTS: user.is_active = False logger.info( "Account disabled due to too many invalid login attempts: %s" % user.username) if config.cripts_email_end_tag: subject = "CRIPTs Account Lockout" + config.cripts_email_subject_tag else: subject = config.cripts_email_subject_tag + "CRIPTs Account Lockout" body = """ You are receiving this email because your CRIPTs account has been locked out due to too many invalid login attempts. If you did not perform this action, someone may be attempting to access your account. Please contact a site administrator to resolve. """ try: user.email_user(subject, body) except Exception, err: logger.warning("Error sending email: %s" % str(err)) self.track_login_attempt(user, e) user.reload()
def create_zip(files, pw_protect=True): """ Create a zip file. Creates a temporary directory to write files to on disk using :class:`tempfile`. Uses /usr/bin/zip as the zipping mechanism currently. Will password protect the zip file as a default. The password for the zip file defaults to "infected", but it can be changed in the config under zip7_password. :param files: The files to add to the zip file. :type files: list of files which are in the format of a list or tuple of (<filename>, <data>). :param pw_protect: To password protect the zip file or not. :type pw_protect: boolean :returns: :class:`cripts.core.exceptions.ZipFileError`, str """ dumpdir = "" try: # Zip can take data from stdin to compress, but # you can't define the filenames within the archive, # they show up as "-". Therefore, we need to write # out the file, compress it and return the zip. # Save the sample as a file in a temp directory # NOTE: the following line was causing a "permission denied" exception. # Removed dir arg. from cripts.config.config import CRIPTsConfig cripts_config = CRIPTsConfig.objects().first() if cripts_config: zip7_password = cripts_config.zip7_password or 'hashed' else: zip7_password = settings.ZIP7_PASSWORD or 'hashed' dumpdir = tempfile.mkdtemp() #dir=temproot #write out binary files for f in files: filename = f[0] file_data = f[1] # make sure our desired path doesn't already exist (some files may # have the same name but different data) path = dumpdir + "/" + filename.encode("utf-8") i = 1 tmp = path while os.path.exists(tmp): tmp = path+"("+str(i)+")" i += 1 with open(tmp, "wb") as fh: fh.write(file_data) # Build the command line for zip # NOTE: forking subprocess instead of using Python's ZipFile library # because ZipFile does not allow us to create password-protected zip # archives, only read them. # -j don't include original filepath zipname = "zip.zip" #The name we give it doesn't really matter args = ["/usr/bin/zip", "-r", "-j", dumpdir+"/"+zipname, dumpdir] if pw_protect: args += ["-P", zip7_password] args += [dumpdir+"/"+zipname, dumpdir] proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # Give the process 30 seconds to complete, otherwise kill it waitSeconds = 30 while (proc.poll() is None and waitSeconds): time.sleep(1) waitSeconds -= 1 zipdata = "" if proc.returncode: # zip spit out an error errmsg = "Error while creating archive\n" + proc.stdout.read() raise ZipFileError, errmsg elif not waitSeconds: # Process timed out proc.terminate() raise ZipFileError, "Error:\nProcess failed to terminate" else: with open(dumpdir + "/" + zipname, "rb") as fh: zipdata = fh.read() if not len(zipdata): raise ZipFileError, "Error:\nThe zip archive contains no data" return zipdata except ZipFileError: raise except Exception, ex: errmsg = "" for err in ex.args: errmsg = errmsg + " " + unicode(err) raise ZipFileError, errmsg