#ifndef lint static char *RCSid = "$Header: /src/common/usc/lib/libuscgen/RCS/safepwd.c,v 1.50 1996/03/01 21:42:29 mcooper Exp $"; #endif /* * base Gecos rules are from crack4.0/Scripts/gecos.rules * base Dicts rules are from crack4.0/Scripts/dicts.rules * rule defines are from crack4.0/Sources/crack-lib.c * * added dict rule ">2crc" * added dict rules capitalized prepended/appended by 0-9 * * Contents: * char *safepwd(struct passwd *pwd, char *pass) * * static int chk_chars(char *pass) * static int chk_username(char *username, char *pass) * static int chk_gecos(char *gecos, char *pass) * static int chk_dicts(char *gecos, char *pass) * * static int apply_rule(char *rule, char *word, char *munged) * static int reverse_apply_rule(char *rule, char *word, char *munged_word) * * static int dict_lookup_word(char *pass) * static int look(char* word, char *wordfile) * static int compare(register char *s, register char *t) * static int getword(register char *w) * * static int chartoint(char character) * * if HAS_VARARGS * static char *msg(va_list) * else * static char *msg(char *fmt, char *a1, char *a2, char *a3) * */ #if defined(alliant) || defined(NeXT) #define OLDDIR #endif #include #include #include #include #include #include #include #ifdef OLDDIR #include #else #include #endif #ifdef HAS_VARARGS #include #endif #define M_PLEASE "Please use a password that " #define MIN_PW_LENGTH 6 #define NUM_USED_CHARS 8 #define MIN_DIFF_CHARS 3 #define WORDSIZ 257 #if defined __linux__ /*#define STD_WORD_FILE "/usr/dict/words"*/ #define STD_WORD_FILE "/usr/share/dict/words" #else #define STD_WORD_FILE "/usr/lib/dict/words" #endif #define LOCAL_DICT_DIR "/usr/local/dict" #if defined(sunos5) #define MAXNAMLEN 512 #endif #if !defined(S_ISREG) #if defined(S_IFMT) && defined(S_IFREG) #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) #else #define S_ISREG(m) (((m)_IFMT) == S_IFREG) #endif #endif /* S_ISREG */ #define RULE_NOOP ':' #define RULE_PREPEND '^' #define RULE_APPEND '$' #define RULE_REVERSE 'r' #define RULE_UPPERCASE 'u' #define RULE_LOWERCASE 'l' #define RULE_PLURALISE 'p' #define RULE_CAPITALISE 'c' #define RULE_DUPLICATE 'd' #define RULE_REFLECT 'f' #define RULE_SUBSTITUTE 's' #define RULE_MATCH '/' #define RULE_NOT '!' #define RULE_LT '<' #define RULE_GT '>' #define RULE_EXTRACT 'x' #define LOWER_CASE -1 #define NO_CASE 0 #define UPPER_CASE 1 typedef struct { char *rule; char *message; } RuleDef; #ifdef OLDDIR typedef struct direct mdirent; #else typedef struct dirent mdirent; #endif /*extern char *index(), *strcat(), *strncat(), *strcpy(), *strchr(), *strtok();*/ #if !defined(_STDIO_H_) && !defined(_STDIO_H) && !defined(_H_STDIO) && !defined(_STDIO_INCLUDED) && !defined(sgi) extern int printf(), fprintf(); extern int fseek(), fclose(); extern int fgetc(); #endif #if 0 #if !defined(ultrix) char *malloc(), *realloc(); void free(); #else #include #endif #endif char *crypt(); #if !defined(__STDC__) && !defined(tolower) && !defined(_CTYPE_H_) && !defined(_CTYPE_INCLUDED) && !defined(_H_STDIO) && !defined(sunos5) && !defined(sgi) extern char tolower(); extern char toupper(); #endif static int dict_lookup_word(), look(), compare(), getword(); /* these are from mcooper's safepw */ static char *msg(); /* another one from mcooper's safepw */ static int chk_chars(), chk_username(), chk_gecos(), chk_dicts(); static int apply_rule(), reverse_apply_rule(); static int chartoint(); /* static char *null_string = ""; */ /* static char *safe_message = "safe"; */ static char *username_check_error = "username check error"; static char *gecos_check_error = "gecos check error"; static char *dicts_check_error = "dicts check error"; static char message_buf[BUFSIZ]; static char trunc_pass[WORDSIZ]; static int num_gecos_rules, num_dicts_rules; static int has_std_dict, num_local_dicts; static char **local_dicts = NULL; static FILE *dfile; RuleDef Gecos_Rule_Table[] = { { ">2", "derived from GECOS info" }, /* 0 */ { ">2l", "derived from GECOS info" }, /* 1 */ { ">2cr", "derived from GECOS info" }, /* 2 */ { ">2rc", "derived from GECOS info" }, /* 3 */ { ">2ld", "derived from GECOS info" }, /* 4 */ { ">2lf", "derived from GECOS info" }, /* 5 */ { ">2lr", "derived from GECOS info" }, /* 6 */ { ">2u", "derived from GECOS info" }, /* 7 */ { ">2ud", "derived from GECOS info" }, /* 8 */ { ">2uf", "derived from GECOS info" }, /* 9 */ { ">2ur", "derived from GECOS info" }, /* 10 */ { ">2<8$0", "derived from GECOS info" }, /* 11 */ { ">2<8$1", "derived from GECOS info" }, /* 12 */ { ">2<8$2", "derived from GECOS info" }, /* 13 */ { ">2<8$3", "derived from GECOS info" }, /* 14 */ { ">2<8$4", "derived from GECOS info" }, /* 15 */ { ">2<8$5", "derived from GECOS info" }, /* 16 */ { ">2<8$6", "derived from GECOS info" }, /* 17 */ { ">2<8$7", "derived from GECOS info" }, /* 18 */ { ">2<8$8", "derived from GECOS info" }, /* 19 */ { ">2<8$9", "derived from GECOS info" }, /* 20 */ { ">2<8c$!", "derived from GECOS info" }, /* 21 */ { ">2<8c$.", "derived from GECOS info" }, /* 22 */ { ">2<8c$?", "derived from GECOS info" }, /* 23 */ { ">2<8l$!", "derived from GECOS info" }, /* 24 */ { ">2<8l$.", "derived from GECOS info" }, /* 25 */ { ">2<8l$0", "derived from GECOS info" }, /* 26 */ { ">2<8l$1", "derived from GECOS info" }, /* 27 */ { ">2<8l$2", "derived from GECOS info" }, /* 28 */ { ">2<8l$3", "derived from GECOS info" }, /* 29 */ { ">2<8l$4", "derived from GECOS info" }, /* 30 */ { ">2<8l$5", "derived from GECOS info" }, /* 31 */ { ">2<8l$6", "derived from GECOS info" }, /* 32 */ { ">2<8l$7", "derived from GECOS info" }, /* 33 */ { ">2<8l$8", "derived from GECOS info" }, /* 34 */ { ">2<8l$9", "derived from GECOS info" }, /* 35 */ { ">2<8l$?", "derived from GECOS info" }, /* 36 */ { ">2<8u$!", "derived from GECOS info" }, /* 37 */ { ">2<8u$.", "derived from GECOS info" }, /* 38 */ { ">2<8u$0", "derived from GECOS info" }, /* 39 */ { ">2<8u$1", "derived from GECOS info" }, /* 40 */ { ">2<8u$2", "derived from GECOS info" }, /* 41 */ { ">2<8u$3", "derived from GECOS info" }, /* 42 */ { ">2<8u$4", "derived from GECOS info" }, /* 43 */ { ">2<8u$5", "derived from GECOS info" }, /* 44 */ { ">2<8u$6", "derived from GECOS info" }, /* 45 */ { ">2<8u$7", "derived from GECOS info" }, /* 46 */ { ">2<8u$8", "derived from GECOS info" }, /* 47 */ { ">2<8u$9", "derived from GECOS info" }, /* 48 */ { ">2<8u$?", "derived from GECOS info" }, /* 49 */ { ">2^!", "derived from GECOS info" }, /* 50 */ { ">2^0", "derived from GECOS info" }, /* 51 */ { ">2^1", "derived from GECOS info" }, /* 52 */ { ">2^2", "derived from GECOS info" }, /* 53 */ { ">2^3", "derived from GECOS info" }, /* 54 */ { ">2^4", "derived from GECOS info" }, /* 55 */ { ">2^5", "derived from GECOS info" }, /* 56 */ { ">2^6", "derived from GECOS info" }, /* 57 */ { ">2^7", "derived from GECOS info" }, /* 58 */ { ">2^8", "derived from GECOS info" }, /* 59 */ { ">2^9", "derived from GECOS info" }, /* 60 */ { ">2^?", "derived from GECOS info" }, /* 61 */ { ">2l^!", "derived from GECOS info" }, /* 62 */ { ">2l^0", "derived from GECOS info" }, /* 63 */ { ">2l^1", "derived from GECOS info" }, /* 64 */ { ">2l^2", "derived from GECOS info" }, /* 65 */ { ">2l^3", "derived from GECOS info" }, /* 66 */ { ">2l^4", "derived from GECOS info" }, /* 67 */ { ">2l^5", "derived from GECOS info" }, /* 68 */ { ">2l^6", "derived from GECOS info" }, /* 69 */ { ">2l^7", "derived from GECOS info" }, /* 70 */ { ">2l^8", "derived from GECOS info" }, /* 71 */ { ">2l^9", "derived from GECOS info" }, /* 72 */ { ">2l^?", "derived from GECOS info" }, /* 73 */ { ">2cf", "derived from GECOS info" }, /* 74 */ { ">2^($)", "derived from GECOS info" }, /* 75 */ { "", "" } }; RuleDef Dicts_Rule_Table[] = { { ":", "" }, { "l", "lowercased" }, { ">2lp", "lowercased, pluralized" }, { ">2<8l$!", "lowercased with a trailing '!'" }, { ">2<8l$.", "lowercased with a trailing '.'" }, { ">2<8l$0", "lowercased with a trailing '0'" }, { ">2<8l$1", "lowercased with a trailing '1'" }, { ">2<8l$?", "lowercased with a trailing '?'" }, { ">2lr", "lowercased, reversed" }, { ">2c", "capitalized" }, { ">2cr", "capitalized, reversed" }, { ">2u", "uppercased" }, { ">2/lsl1l", "lowercased with 'l's replaced by '1's" }, { ">2/oso0l", "lowercased with 'o's replaced by '0's" }, { ">2/l/osl1so0l", "lowercased with 'l's replaced by '1's and 'o's replaced by '0's" }, { ">2lf", "lowercased, reflected" }, { ">2<8$ :", "with a trailing ' '" }, { ">2<8l$2", "lowercased with a trailing '2'" }, { ">2<8l$3", "wercased with a trailing '3'" }, { ">2<8l$4", "lowercased with a trailing '4'" }, { ">2<8l$5", "lowercased with a trailing '5'" }, { ">2<8l$6", "lowercased with a trailing '6'" }, { ">2<8l$7", "lowercased with a trailing '7'" }, { ">2<8l$8", "lowercased with a trailing '8'" }, { ">2<8l$9", "lowercased with a trailing '9'" }, { ">2rc", "reversed, capitalized" }, { ">2<8c$!", "capitalized with a trailing '!'" }, { ">2<8c$.", "capitalized with a trailing '.'" }, { ">2<8c$?", "capitalized with a trailing '?'" }, { ">2ld", "lowercased, duplicated" }, { ">2<8u$!", "uppercased with a trailing '!'" }, { ">2<8u$.", "uppercased with a trailing '.'" }, { ">2<8u$?", "uppercased with a trailing '?'" }, { ">2/OsO0u", "with 'O's replaced by '0's" }, { ">2l^!", "lowercased with a leading '!'" }, { ">2l^0", "lowercased with a leading '0'" }, { ">2l^1", "lowercased with a leading '1'" }, { ">2l^2", "lowercased with a leading '2'" }, { ">2l^3", "lowercased with a leading '3'" }, { ">2l^4", "lowercased with a leading '4'" }, { ">2l^5", "lowercased with a leading '5'" }, { ">2l^6", "lowercased with a leading '6'" }, { ">2l^7", "lowercased with a leading '7'" }, { ">2l^8", "lowercased with a leading '8'" }, { ">2l^9", "lowercased with a leading '9'" }, { ">2l^?", "lowercased with a leading '?'" }, { ">2c^!", "capitalized with a leading '!'" }, { ">2c^0", "capitalized with a leading '0'" }, { ">2c^1", "capitalized with a leading '1'" }, { ">2c^2", "capitalized with a leading '2'" }, { ">2c^3", "capitalized with a leading '3'" }, { ">2c^4", "capitalized with a leading '4'" }, { ">2c^5", "capitalized with a leading '5'" }, { ">2c^6", "capitalized with a leading '6'" }, { ">2c^7", "capitalized with a leading '7'" }, { ">2c^8", "capitalized with a leading '8'" }, { ">2c^9", "capitalized with a leading '9'" }, { ">2c^?", "capitalized with a leading '?'" }, { ">2c$0", "capitalized with a trailing '0'" }, { ">2c$1", "capitalized with a trailing '1'" }, { ">2c$2", "capitalized with a trailing '2'" }, { ">2c$3", "capitalized with a trailing '3'" }, { ">2c$4", "capitalized with a trailing '4'" }, { ">2c$5", "capitalized with a trailing '5'" }, { ">2c$6", "capitalized with a trailing '6'" }, { ">2c$7", "capitalized with a trailing '7'" }, { ">2c$8", "capitalized with a trailing '8'" }, { ">2c$9", "capitalized with a trailing '9'" }, { ">2crc", "capitalized, reversed, capitalized" }, { ">2rcr", "with the last letter capitalized" }, { ">2ud", "uppercased, duplicated" }, { ">2uf", "uppercased, reflected" }, { ">2ur", "uppercased, reversed" }, { "", "" } }; /* ============================================================================================================================= */ char *safepwd(pwd, pass) struct passwd *pwd; char *pass; { int prev_case = NO_CASE, curr_case = NO_CASE; char *cp; /* if password is safe, return NULL. if password not safe, return reason. if *pwd is (struct passwd *)NULL, then we can't do any GCOS checking. if *pwd->pw_passwd is (char *)NULL, then the user currently has no password if no dicts found, return (char *)NULL (OK) but print error message to stderr */ int pass_len; int chk_return; #if defined(DEBUG) /* dump parameters we got */ printf ("%s:%s:%d:%d:%s:%s:%s\n", pwd->pw_name, pwd->pw_passwd, pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); printf ("pass: [%s]\n", pass); #endif /* preliminary password rules */ if ((pass == NULL) || ((pass_len = strlen(pass)) == 0)) return msg("is not empty"); if (pass_len < MIN_PW_LENGTH) return msg("is more than %d characters", MIN_PW_LENGTH); /* if (pwd != (struct passwd *)NULL) { if ((pwd->pw_passwd) && (strlen(pwd->pw_passwd) > 0)) { if (strcmp ((char *)pwd->pw_passwd, crypt(pass, (char *)pwd->pw_passwd)) == 0) return msg("is not your current password"); } else { fprintf (stderr, "WARNING: user currently has no password\n"); } } */ if (strchr(pass, ':') != NULL) return msg("does not contain a colon ':'"); for (cp = pass; *cp != '\0'; cp++) if (iscntrl(*cp)) return msg("does not contain a control character"); /* disallow passwords that are all lower or all upper case */ /* ignore non alphabetic characters */ for (cp = pass; *cp != '\0'; cp++) { if (!isalpha(*cp)) continue; if (prev_case == NO_CASE) curr_case = prev_case = islower(*cp) ? LOWER_CASE : UPPER_CASE; else if ((curr_case = islower(*cp) ? LOWER_CASE : UPPER_CASE) != prev_case) break; } if (curr_case == prev_case) return msg("is not all %s characters", (curr_case == LOWER_CASE) ? "lowercase" : "uppercase"); #if defined(MIN_DIFF_CHARS) if (chk_chars(pass)) return msg("contains at least %d different characters", MIN_DIFF_CHARS); #endif /* Count rules */ for (num_gecos_rules = 0; Gecos_Rule_Table[num_gecos_rules].rule[0] != '\0'; num_gecos_rules++); for (num_dicts_rules = 0; Dicts_Rule_Table[num_dicts_rules].rule[0] != '\0'; num_dicts_rules++); /* apply gecos rules to gecos information from user supplied passwd struct. foreach dict rule reverse apply dict rule to user supplied password if necessary. if standard dictionary exists, look up resulting string. if local dictionaries exist, look up resulting string in each. */ strcpy(trunc_pass, pass); /* Only do username and gcos checks if we were passed a struct passwd */ if (pwd != (struct passwd *)NULL) { chk_return = chk_username(pwd->pw_name, trunc_pass); if ((chk_return < 0) || (chk_return > num_dicts_rules)) return username_check_error; else if (chk_return > 0) return msg("is not your username %s", Dicts_Rule_Table[chk_return-1].message); chk_return = chk_gecos(pwd->pw_gecos, trunc_pass); if ((chk_return < 0) || (chk_return > num_gecos_rules)) return gecos_check_error; else if (chk_return > 0) return msg("is not %s", Gecos_Rule_Table[chk_return-1].message); } strncpy(trunc_pass, pass, NUM_USED_CHARS); trunc_pass[NUM_USED_CHARS] = '\0'; chk_return = chk_dicts(trunc_pass); if ((chk_return < 0) || (chk_return > num_dicts_rules)) return dicts_check_error; else if (chk_return > 0) return msg("is not a dictionary word %s", Dicts_Rule_Table[chk_return-1].message); /* return null_string; */ return NULL; } /* end of safepwd */ /* ============================================================================================================================= */ static int chk_chars(pass) char *pass; { #if defined(MIN_DIFF_CHARS) register char *cp2; register int i; int charset; char chartab[256]; /* * Make sure password uses a rich enough character set. * Borrowed from the Ultrix 4.1 passwd program. */ charset = 0; #if defined(sunos5) memset(chartab, 0, sizeof chartab); #else bzero(chartab, sizeof chartab); #endif for (cp2 = pass; *cp2; cp2++) chartab[*cp2]++; for ( i = 0; i < sizeof chartab; i++) if (chartab[i]) charset++; return (charset < MIN_DIFF_CHARS) ? 1 : 0; #endif } /* end of chk_chars */ static int chk_username(username, pass) char *username; char *pass; { /* return values: -1 : error 0 : safe pwd n : rule which failed + 1 */ static char munged_word[WORDSIZ]; int curr_rule; #if defined(DEBUG) printf ("in: chk_username\n"); printf (" username [%s], pass [%s]\n", username, pass); printf (" %d dicts rules\n", num_dicts_rules); #endif /* go though all dicts rules on username */ for (curr_rule = 0; curr_rule < num_dicts_rules; curr_rule++) if (apply_rule(Dicts_Rule_Table[curr_rule].rule, username, munged_word)) if (strcmp(pass, munged_word) == 0) { #if defined(DEBUG) printf (" cracked using username with dicts rule: %d\n", curr_rule); #endif return curr_rule+1; } return 0; } /* end of chk_username */ static int chk_gecos(gecos_string, pass) char *gecos_string; char *pass; { /* return values: -1 : error 0 : safe pwd n : rule which failed + 1 */ static char munged_word[WORDSIZ]; static char combined_words[WORDSIZ]; char *words_buffer, *ptr; char **words_array; int num_words, curr_rule, i, j; int gecos_len; #if defined(DEBUG) printf ("in: chk_gecos\n"); printf (" gecos [%s], pass [%s]\n", gecos_string, pass); printf (" %d gecos rules\n", num_gecos_rules); #endif if ((gecos_string == NULL) || ((gecos_len = strlen(gecos_string)) == 0)) return 0; if ((words_buffer = (char *)malloc(gecos_len + 1)) == (char *)NULL) { fprintf (stderr, "malloc failed\n"); return -1; } strcpy(words_buffer, gecos_string); /* replace all punctuation with space and lowercase */ for (ptr = words_buffer; *ptr != '\0'; ptr++) { if (ispunct(*ptr)) *ptr = ' '; /* else *ptr = tolower(*ptr); */ } #if defined(DEBUG) printf ("after ispunct: [%s]\n", words_buffer); #endif num_words = 0; if ((ptr = (char *)strtok(words_buffer, " ")) != (char *)NULL) { if ((words_array = (char **)malloc(sizeof(char *))) == (char **)NULL) { fprintf (stderr, "malloc failed\n"); return -1; } words_array[0] = ptr; num_words++; while ((ptr = (char *)strtok(NULL, " ")) != (char *)NULL) { if ((words_array = (char **)realloc(words_array, (num_words + 1) * sizeof(char *))) == (char **)NULL) { fprintf (stderr, "realloc failed\n"); return -1; } words_array[num_words] = ptr; num_words++; } } /* go through all gecos rules for each word */ for (i = 0; i < num_words; i++) for (curr_rule = 0; curr_rule < num_gecos_rules; curr_rule++) if (apply_rule(Gecos_Rule_Table[curr_rule].rule, words_array[i], munged_word)) if (strcmp(pass, munged_word) == 0) { #if defined(DEBUG) printf (" cracked using gecos words with rule: %d\n", curr_rule); #endif return curr_rule+1; } /* try combinations of words, skip initials */ for (j = 1; j < num_words; j++) for (i = 0; i < j; i++) { /* skip if an initial */ if ((words_array[i][1] == '\0') || (words_array[j][1] == '\0')) continue; sprintf(combined_words, "%s%s", words_array[i], words_array[j]); for (curr_rule = 0; curr_rule < num_gecos_rules; curr_rule++) if (apply_rule(Gecos_Rule_Table[curr_rule].rule, combined_words, munged_word)) if (strcmp(pass, munged_word) == 0) { #if defined(DEBUG) printf (" cracked using gecos word combinations with rule: %d\n", curr_rule); #endif return curr_rule+1; } } /* try initials + word */ for (j = 1; j < num_words; j++) for (i = 0; i < j; i++) { sprintf(combined_words, "%c%s", words_array[i][0], words_array[j]); if (islower(combined_words[0])) combined_words[0] = toupper(combined_words[0]); for (curr_rule = 0; curr_rule < num_gecos_rules; curr_rule++) if (apply_rule(Gecos_Rule_Table[curr_rule].rule, combined_words, munged_word)) if (strcmp(pass, munged_word) == 0) { #if defined(DEBUG) printf (" cracked using gecos initials+words with rule: %d\n", curr_rule); #endif return curr_rule+1; } } free(words_buffer); free((char *)words_array); return 0; } /* end of chk_gecos */ static int chk_dicts(pass) char *pass; { /* return values: -1 : internal error, in reverse_apply_rule 0 : safe pwd n : rule which failed + 1 */ DIR *main_dirp = NULL; mdirent *tmp_dirp = NULL; struct stat stat_result; char tmp_dict_filename[BUFSIZ]; static char munged_word[BUFSIZ]; int curr_rule; /* bypass, never use std dict since all words are in my dict, and correctly sorted */ has_std_dict = 0; /* see if additional dictionaries are there */ if ((main_dirp = opendir(LOCAL_DICT_DIR)) != NULL) { /* build array of dictionary dir entries */ num_local_dicts = 0; while (tmp_dirp = readdir(main_dirp)) { /* skip special files */ #if defined(sunos5) || defined(sgi) if (tmp_dirp->d_name[0]=='.' && (tmp_dirp->d_name[1]=='\0' || (tmp_dirp->d_name[1]=='.' && tmp_dirp->d_name[2]=='\0'))) #else /* !sunos5 */ if ((strncmp((char *)tmp_dirp->d_name, ".", (int)tmp_dirp->d_namlen) == 0) || (strncmp((char *)tmp_dirp->d_name, "..", (int)tmp_dirp->d_namlen) == 0)) #endif /* sunos5 */ continue; /* create full pathname to file */ sprintf(tmp_dict_filename, "%s/%s", LOCAL_DICT_DIR, (char *)tmp_dirp->d_name); #ifdef DEBUG fprintf(stderr, "safepwd: checking dictionary [%s]\n", tmp_dict_filename); #endif /* skip if we can't read the file or if it isn't a regular file */ if (stat(tmp_dict_filename, &stat_result) != 0) { fprintf(stderr, "safepwd: can't read file\n"); continue; } if (S_ISREG(stat_result.st_mode) == 0) { fprintf(stderr, "safepwd, not a regular file\n"); continue; } if (num_local_dicts == 0) local_dicts = (char **)malloc(sizeof(char *)); else local_dicts = (char **)realloc(local_dicts, (num_local_dicts + 1) * sizeof(char *)); if (local_dicts == (char **)NULL) { fprintf (stderr, "malloc/realloc failed\n"); return -1; } if ((local_dicts[num_local_dicts] = (char *)malloc(strlen(tmp_dict_filename) + 1)) == (char *)NULL) { fprintf (stderr, "malloc failed\n"); return -1; } strcpy(local_dicts[num_local_dicts], tmp_dict_filename); num_local_dicts++; } closedir(main_dirp); } else num_local_dicts = -1; if (!has_std_dict && (num_local_dicts <= 0)) { fprintf (stderr, "no dictionaries available - cannot check password\n"); /* return -1; */ return 0; } #if defined(DEBUG) printf ("in: chk_dicts\n"); printf (" pass [%s]\n", pass); printf (" %d dicts rules\n", num_dicts_rules); printf (" %s standard dict: [%s]\n", has_std_dict ? "has" : "doesn't have", STD_WORD_FILE); printf (" %d local dictionaries\n", num_local_dicts); #endif /* go though all dicts rules on user supplied password */ for (curr_rule = 0; curr_rule < num_dicts_rules; curr_rule++) { switch (reverse_apply_rule(Dicts_Rule_Table[curr_rule].rule, pass, munged_word)) { case -1: /* something went wrong during munging */ return -1; case 0: /* no munging done, no lookup */ break; case 1: /* word munged, do lookup */ if (dict_lookup_word(munged_word)) { #if defined(DEBUG) printf (" cracked using rule: %d\n", curr_rule); #endif return curr_rule+1; } break; } } return 0; } /* end of chk_dicts */ /* ============================================================================================================================= */ static int apply_rule(rule, word, munged) char *rule; char *word; char *munged; { /* return values: -1 : internal error, malloc/realloc failed 0 : rule not applied 1 : rule applied */ char *rule_ptr, *tmp_ptr; int munged_len; int do_check; int i, tmp_num_1, tmp_num_2; #if defined(DEBUG) printf ("in: apply_rule\n"); printf (" rule [%s], word [%s]\n", rule, word); #endif do_check = 0; if ((munged_len = strlen(word)) == 0) return 0; strcpy(munged, word); for (rule_ptr = rule; *rule_ptr != '\0'; rule_ptr++) { switch (*rule_ptr) { case RULE_NOOP: #if defined(DEBUG) printf (" RULE_NOOP\n"); #endif /* kludge to force dictionary lookup */ do_check = 1; break; case RULE_PREPEND: #if defined(DEBUG) printf (" RULE_PREPEND\n"); #endif if (*(rule_ptr+1) == '\0') return -1; #if defined(DEBUG) printf (" prepend [%c]\n", *(rule_ptr+1)); #endif if ((tmp_ptr = (char *)malloc(munged_len+2)) == (char *)NULL) { fprintf (stderr, "malloc failed\n"); return -1; } strcpy(tmp_ptr, munged); munged[0] = *(rule_ptr+1); strcpy(munged+1, tmp_ptr); *(munged+munged_len+1) = '\0'; free(tmp_ptr); munged_len++; rule_ptr++; do_check = 1; break; case RULE_APPEND: #if defined(DEBUG) printf (" RULE_APPEND\n"); #endif if (*(rule_ptr+1) == '\0') return -1; #if defined(DEBUG) printf (" append [%c]\n", *(rule_ptr+1)); #endif munged[munged_len] = *(rule_ptr+1); munged[munged_len+1] = '\0'; munged_len++; rule_ptr++; do_check = 1; break; case RULE_REVERSE: #if defined(DEBUG) printf (" RULE_REVERSE\n"); #endif if ((tmp_ptr = (char *)malloc(munged_len+1)) == (char *)NULL) { fprintf (stderr, "malloc failed\n"); return -1; } for (i = 0; i < munged_len; i++) tmp_ptr[i] = munged[munged_len - 1 - i]; strncpy(munged, tmp_ptr, munged_len); munged[munged_len] = '\0'; free(tmp_ptr); do_check = 1; break; case RULE_UPPERCASE: #if defined(DEBUG) printf (" RULE_UPPERCASE\n"); #endif for (i = 0; i < munged_len; i++) if (islower(munged[i])) { munged[i] = toupper(munged[i]); do_check = 1; } break; case RULE_LOWERCASE: #if defined(DEBUG) printf (" RULE_LOWERCASE\n"); #endif for (i = 0; i < munged_len; i++) if (isupper(munged[i])) { munged[i] = tolower(munged[i]); do_check = 1; } break; case RULE_PLURALISE: #if defined(DEBUG) printf (" RULE_PLURALISE\n"); #endif if ((strncmp(munged+(munged_len-4), "ch", 2) == 0) || (strncmp(munged+(munged_len-4), "ex", 2) == 0) || (strncmp(munged+(munged_len-4), "ix", 2) == 0) || (strncmp(munged+(munged_len-4), "sh", 2) == 0) || (strncmp(munged+(munged_len-4), "ss", 2) == 0)) { strcpy(munged+munged_len, "es"); munged_len += 2; } else if ((munged_len > 2) && (munged[munged_len-1] == 'y')) { if (strchr("aeiou", munged[munged_len-1]) != NULL) { strcpy(munged+munged_len, "s"); munged_len++; } else { strcpy(munged+munged_len-1, "ies"); munged_len += 2; } } else if (munged[munged_len-1] == 's') { strcpy(munged+munged_len, "es"); munged_len += 2; } else { strcpy(munged+munged_len, "s"); munged_len++; } do_check = 1; break; case RULE_CAPITALISE: #if defined(DEBUG) printf (" RULE_CAPITALISE\n"); #endif if (islower(munged[0])) { munged[0] = toupper(munged[0]); do_check = 1; } break; case RULE_DUPLICATE: #if defined(DEBUG) printf (" RULE_DUPLICATE\n"); #endif if ((tmp_ptr = (char *)malloc(munged_len+1)) == (char *)NULL) { fprintf (stderr, "malloc failed\n"); return -1; } strcpy(tmp_ptr, munged); strcpy(munged+munged_len, tmp_ptr); munged_len *= 2; free(tmp_ptr); do_check = 1; break; case RULE_REFLECT: #if defined(DEBUG) printf (" RULE_REFLECT\n"); #endif if ((tmp_ptr = (char *)malloc(munged_len+1)) == (char *)NULL) { fprintf (stderr, "malloc failed\n"); return -1; } for (i = 0; i < munged_len; i++) tmp_ptr[i] = munged[munged_len - 1 - i]; strncpy(munged+munged_len, tmp_ptr, munged_len); munged_len *= 2; munged[munged_len] = '\0'; free(tmp_ptr); do_check = 1; break; case RULE_SUBSTITUTE: #if defined(DEBUG) printf (" RULE_SUBSTITUTE\n"); #endif if (*(rule_ptr+1) == '\0') return -1; else if (*(rule_ptr+2) == '\0') return -1; #if defined(DEBUG) printf (" substitute [%c] with [%c]\n", *(rule_ptr+1), *(rule_ptr+2)); #endif for (i = 0; i < munged_len; i++) if (munged[i] == *(rule_ptr+1)) { munged[i] = *(rule_ptr+2); do_check = 1; } rule_ptr += 2; break; case RULE_MATCH: #if defined(DEBUG) printf (" RULE_MATCH\n"); #endif if (*(rule_ptr+1) == '\0') return -1; #if defined(DEBUG) printf (" match [%c]\n", *(rule_ptr+1)); #endif if (strchr(munged, *(rule_ptr+1)) == (char *)NULL) return 0; else do_check = 1; rule_ptr++; break; case RULE_NOT: #if defined(DEBUG) printf (" RULE_NOT\n"); #endif if (*(rule_ptr+1) == '\0') return -1; #if defined(DEBUG) printf (" not [%c]\n", *(rule_ptr+1)); #endif if (strchr(munged, *(rule_ptr+1)) != (char *)NULL) return 0; else do_check = 1; rule_ptr++; break; case RULE_LT: #if defined(DEBUG) printf (" RULE_LT\n"); #endif if (*(rule_ptr+1) == '\0') return -1; #if defined(DEBUG) printf (" lt [%c]\n", *(rule_ptr+1)); #endif tmp_num_1 = chartoint(*(rule_ptr+1)); if (munged_len >= tmp_num_1) return 0; else do_check = 1; rule_ptr++; break; case RULE_GT: #if defined(DEBUG) printf (" RULE_GT\n"); #endif if (*(rule_ptr+1) == '\0') return -1; #if defined(DEBUG) printf (" gt [%c]\n", *(rule_ptr+1)); #endif tmp_num_1 = chartoint(*(rule_ptr+1)); if (munged_len <= tmp_num_1) return 0; else do_check = 1; rule_ptr++; break; case RULE_EXTRACT: #if defined(DEBUG) printf (" RULE_EXTRACT\n"); #endif if (*(rule_ptr+1) == '\0') return -1; else if (*(rule_ptr+2) == '\0') return -1; tmp_num_1 = chartoint(*(rule_ptr+1)); tmp_num_2 = chartoint(*(rule_ptr+2)); #if defined(DEBUG) printf (" starting at pos [%c] extract [%c] chars\n", *(rule_ptr+1), *(rule_ptr+2)); #endif if ((tmp_ptr = (char *)malloc(tmp_num_2 + 1)) == (char *)NULL) { fprintf (stderr, "malloc failed\n"); return -1; } strncpy(tmp_ptr, munged+tmp_num_1, tmp_num_2); munged[tmp_num_2] = '\0'; munged_len = tmp_num_2; rule_ptr += 2; do_check = 1; break; } } #if defined(DEBUG) printf (" munged [%s], do_check %d\n", munged, do_check); #endif return do_check; } /* end of apply_rule */ static int reverse_apply_rule(rule, word, munged) char *rule; char *word; char *munged; { /* return values: -1 : internal error, malloc/realloc failed 0 : rule not applied 1 : rule applied */ char *rule_ptr, *tmp_ptr; char **op_array; int munged_len, tmp_len; int num_ops, curr_op; int do_check; int i; #if defined(DEBUG) printf ("in: reverse_apply_rule\n"); printf (" rule [%s], word [%s]\n", rule, word); #endif do_check = 0; if (strlen(word) == 0) return 0; /* break up rule into individual operations */ for (op_array = (char **)NULL, num_ops = 0, rule_ptr = rule; *rule_ptr != '\0'; rule_ptr++) { if (op_array == (char **)NULL) { if ((op_array = (char **)malloc(sizeof(char *))) == (char **)NULL) { fprintf (stderr, "malloc error\n"); return -1; } } else if ((op_array = (char **)realloc(op_array, (num_ops + 1) * sizeof(char *))) == (char **)NULL) { fprintf (stderr, "realloc error\n"); return -1; } switch (*rule_ptr) { /* no arguments */ case RULE_NOOP: case RULE_REVERSE: case RULE_UPPERCASE: case RULE_LOWERCASE: case RULE_PLURALISE: case RULE_CAPITALISE: case RULE_DUPLICATE: case RULE_REFLECT: if ((op_array[num_ops] = (char *)malloc(2)) == (char *)NULL) { fprintf (stderr, "malloc error\n"); return -1; } op_array[num_ops][0] = *rule_ptr; op_array[num_ops][1] = '\0'; break; /*one argument */ case RULE_PREPEND: case RULE_APPEND: case RULE_MATCH: case RULE_NOT: case RULE_LT: case RULE_GT: if ((op_array[num_ops] = (char *)malloc(3)) == (char *)NULL) { fprintf (stderr, "malloc error\n"); return -1; } op_array[num_ops][0] = *rule_ptr; op_array[num_ops][1] = *(rule_ptr+1); op_array[num_ops][2] = '\0'; rule_ptr++; break; /* two arguments */ case RULE_SUBSTITUTE: case RULE_EXTRACT: if ((op_array[num_ops] = (char *)malloc(4)) == (char *)NULL) { fprintf (stderr, "malloc error\n"); return -1; } op_array[num_ops][0] = *rule_ptr; op_array[num_ops][1] = *(rule_ptr+1); op_array[num_ops][2] = *(rule_ptr+2); op_array[num_ops][3] = '\0'; rule_ptr += 2; break; default: fprintf (stderr, "reverse_apply_rule: unknown command: [%c]\n", *(rule_ptr)); } num_ops++; } strcpy(munged, word); munged_len = strlen(munged); /* reverse apply operations in reverse order */ for (curr_op = num_ops -1; curr_op >= 0; curr_op--) { switch (op_array[curr_op][0]) { case RULE_NOOP: #if defined(DEBUG) printf (" RULE_NOOP\n"); #endif /* kludge to force dictionary lookup */ do_check = 1; break; case RULE_PREPEND: #if defined(DEBUG) printf (" RULE_PREPEND: [%c]\n", op_array[curr_op][1]); #endif if (munged[0] == op_array[curr_op][1]) { if ((tmp_ptr = (char *)malloc(munged_len)) == (char *)NULL) { fprintf (stderr, "malloc error\n"); return -1; } strncpy(tmp_ptr, munged+1, munged_len - 1); strncpy(munged, tmp_ptr, munged_len - 1); munged[munged_len-1] = '\0'; munged_len--; do_check = 1; free(tmp_ptr); } break; case RULE_APPEND: #if defined(DEBUG) printf (" RULE_APPEND: [%c]\n", op_array[curr_op][1]); #endif if (munged[munged_len-1] == op_array[curr_op][1]) { munged[munged_len-1] = '\0'; munged_len--; do_check = 1; } break; case RULE_REVERSE: #if defined(DEBUG) printf (" RULE_REVERSE\n"); #endif if ((tmp_ptr = (char *)malloc(munged_len+1)) == (char *)NULL) { fprintf (stderr, "malloc error\n"); return -1; } for (i = 0; i < munged_len; i++) tmp_ptr[i] = munged[munged_len - 1 - i]; strncpy(munged, tmp_ptr, munged_len); munged[munged_len] = '\0'; do_check = 1; free(tmp_ptr); break; case RULE_UPPERCASE: #if defined(DEBUG) printf (" RULE_UPPERCASE\n"); #endif do_check = 1; for (i = 0; i < munged_len; i++) if (isupper(munged[i])) munged[i] = tolower(munged[i]); else do_check = 0; break; case RULE_LOWERCASE: #if defined(DEBUG) printf (" RULE_LOWERCASE\n"); #endif break; case RULE_PLURALISE: #if defined(DEBUG) printf (" RULE_PLURALISE\n"); #endif if (munged[munged_len-1] != 's') break; if (strncmp(munged+(munged_len-3), "ies", 3) == 0) { /* replace 'ies' with 'y' */ munged[munged_len-3] = 'y'; munged[munged_len-2] = '\0'; munged_len -= 2; do_check = 1; } else if ((strncmp(munged+(munged_len-2), "es", 2) == 0) && (munged_len > 4)) { if ((strncmp(munged+(munged_len-4), "ch", 2) == 0) || (strncmp(munged+(munged_len-4), "ex", 2) == 0) || (strncmp(munged+(munged_len-4), "ix", 2) == 0) || (strncmp(munged+(munged_len-4), "sh", 2) == 0) || (strncmp(munged+(munged_len-4), "ss", 2) == 0) || (strchr("aeiou", munged[munged_len-4]) != NULL)) { /* strip 'es' */ munged[munged_len-2] = '\0'; munged_len -= 2; do_check = 1; } else { /* strip 's' */ munged[munged_len-1] = '\0'; munged_len--; do_check = 1; } } else { /* strip 's' */ munged[munged_len-1] = '\0'; munged_len--; do_check = 1; } break; case RULE_CAPITALISE: #if defined(DEBUG) printf (" RULE_CAPITALISE\n"); #endif if (isupper(munged[0])) { munged[0] = tolower(munged[0]); do_check = 1; } break; case RULE_DUPLICATE: #if defined(DEBUG) printf (" RULE_DUPLICATE\n"); #endif if ((munged_len % 2) == 0) { tmp_len = munged_len/2; if (strncmp(munged, munged+tmp_len, tmp_len) == 0) { munged[tmp_len] = '\0'; munged_len = tmp_len; do_check = 1; } } break; case RULE_REFLECT: #if defined(DEBUG) printf (" RULE_REFLECT\n"); #endif if ((munged_len % 2) == 0) { tmp_len = munged_len/2; if ((tmp_ptr = (char *)malloc(tmp_len + 1)) == (char *)NULL) { fprintf (stderr, "malloc error\n"); return -1; } for (i = 0; i < tmp_len; i++) tmp_ptr[i] = munged[munged_len - 1 - i]; if (strncmp(munged, tmp_ptr, tmp_len) == 0) { munged[tmp_len] = '\0'; munged_len = tmp_len; do_check = 1; } free(tmp_ptr); } break; case RULE_SUBSTITUTE: #if defined(DEBUG) printf (" RULE_SUBSTITUTE: [%c] [%c]\n", op_array[curr_op][1], op_array[curr_op][2]); #endif for (i = 0; i < munged_len; i++) if (munged[i] == op_array[curr_op][2]) { munged[i] = op_array[curr_op][1]; do_check = 1; } break; case RULE_MATCH: #if defined(DEBUG) printf (" RULE_MATCH: [%c]\n", op_array[curr_op][1]); #endif break; case RULE_NOT: #if defined(DEBUG) printf (" RULE_NOT: [%c]\n", op_array[curr_op][1]); #endif break; case RULE_LT: #if defined(DEBUG) printf (" RULE_LT: [%c]\n", op_array[curr_op][1]); #endif break; case RULE_GT: #if defined(DEBUG) printf (" RULE_GT: [%c]\n", op_array[curr_op][1]); #endif break; case RULE_EXTRACT: #if defined(DEBUG) printf (" RULE_EXTRACT: [%c] [%c]\n", op_array[curr_op][1], op_array[curr_op][2]); #endif break; default: fprintf (stderr, "reverse_apply_rule: unknown command: [%c]\n", op_array[curr_op]); } } for (curr_op = 0; curr_op < num_ops; curr_op++) free(op_array[curr_op]); free(op_array); #if defined(DEBUG) printf (" munged [%s], do_check %d\n", munged, do_check); #endif return do_check; } /* end of reverse_apply_rule */ /* ============================================================================================================================= */ static int dict_lookup_word(pass) char *pass; { int i; #if defined(DEBUG) printf ("in: dict_lookup_word\n"); printf (" pass [%s]\n", pass); #endif if (has_std_dict) if (look(pass, STD_WORD_FILE)) { #if defined(DEBUG) printf (" found in std dictionary: [%s]\n", STD_WORD_FILE); #endif return 1; } for (i = 0; i < num_local_dicts; i++) if (look(pass, local_dicts[i]) == 1) { #if defined(DEBUG) printf (" found in dictionary [%s]\n", local_dicts[i]); #endif return 1; } return 0; } /* end of dict_lookup_word */ /* * Most of look() is from: * "@(#)look.c 1.6 88/02/08 SMI"; from UCB 4.2 07/02/81 */ static int look(word, wordfile) char *word; char *wordfile; { register int c, c_result; char entry[WORDSIZ]; long top, bot, mid; if ((dfile = fopen(wordfile, "r")) == NULL) return 0; #if defined(DEBUG) printf ("in: look\n"); printf (" word [%s], wordfile [%s]\n", word, wordfile); #endif bot = 0; fseek(dfile,0L,2); top = ftell(dfile); for (;;) { mid = (top+bot)/2; fseek(dfile,mid,0); do { c = fgetc(dfile); mid++; } while (c != EOF && c != '\n'); if (!getword(entry)) break; #if defined(DEBUG_COMPARE) printf ("compare(%s, %s)\n", word, entry); #endif c_result = compare(word, entry); #if defined(DEBUG_COMPARE) printf ("c_result %d\n", c_result); #endif switch(c_result) { case -2: case -1: case 0: if (top <= mid) break; top = mid; continue; case 1: case 2: bot = mid; continue; } break; } fseek(dfile,bot,0); while (ftell(dfile)= '0' && character <= '9') return (character - '0'); else if (character >= 'a' && character <= 'z') return (character - 'a' + 10); else if (character >= 'A' && character <= 'Z') return (character - 'A' + 10); else return (-1); } /* end of chartoint */ /* ============================================================================================================================= */ #ifdef HAS_VARARGS static char *msg(va_alist) va_dcl { va_list args; char *fmt, buf[BUFSIZ]; va_start(args); fmt = va_arg(args, char *); vsprintf(buf, fmt, args); va_end(args); sprintf(message_buf, "%s%s", M_PLEASE, buf); return(message_buf); } #else /* !HAS_VARARGS */ static char *msg(fmt, a1, a2, a3) char *fmt; { char buf[BUFSIZ]; sprintf(buf, fmt, a1, a2, a3); sprintf(message_buf, "%s%s", M_PLEASE, buf); return(message_buf); } #endif /* HAS_VARARGS */ /* * goodpasswd - Front end to safepwd() that doesn't * need a passwd struct passed to it. */ char *goodpasswd(pass, username, fullname) char *pass; char *username; char *fullname; { struct passwd p; #if defined(sunos5) memset(&p, 0, sizeof(p)); #else bzero((char *) &p, sizeof(p)); #endif p.pw_name = username; p.pw_gecos = fullname; /* pass fake encrypted password so safepwd doesn't complain about user having no current password */ p.pw_passwd = "ZZZZZZZZZZZZZ"; return(safepwd(&p, pass)); } /* end of goodpasswd */