1CrackLib: A ProActive Password Sanity Library 2By: Alec Muffett 3Address: alecm@crypto.dircon.co.uk 4Date: Sun Dec 14 22:16:48 GMT 1997 5 6- This software is not my fault in any way, nor indeed anybody's - 7 8 9*** What is CrackLib *** 10 11 12CrackLib is a library containing a C function (well, lots of functions 13really, but you only need to use one of them) which may be used in a 14"passwd"-like program. 15 16The idea is simple: try to prevent users from choosing passwords that 17could be guessed by "Crack" by filtering them out, at source. 18 19CrackLib is an offshoot of the the version 5 "Crack" software, and 20contains a considerable number of ideas nicked from the new software. 21 22At the time of writing, Crack 5 is incomplete (still awaiting purchase 23of my home box) - but I though I could share this with you. 24 25[ Incidentally, if Dell or anyone would like to "donate" a Linuxable 26486DX2-66MHz box (EISA/16Mb RAM/640MB HD/AHA1740) as a development 27platform for Crack, I'd be more than grateful to hear from you. 8-) ] 28 29 30NOTE THIS WELL: CrackLib is NOT a replacement "passwd" program. 31CrackLib is a LIBRARY. CrackLib is what trendy marketdroid types would 32probably call an "enabler". 33 34The idea is that you wire it into your _own_ "passwd" program (if you 35have source); alternatively, you wire it into something like "shadow" 36from off of the net. You can use it in other things, too. 37 38You can use it almost _everywhere_. 39 40 41*** Advantages of CrackLib *** 42 43 441) it WORKS! 45 46I wrote something similar ("goodpass") a few years back, which went out 47with Crack v3.x. 48 49Goodpass was slow and buggy and I think it was used (at least in part) 50in "npasswd". Hopefully, CrackLib will supplant "goodpass" entirely. 51 52 532) it's FAST! 54 55CrackLib finds potential passwords quickly, by using an index file to 56access dictionary words, and by keeping a table to assist binary 57searching. 58 59 603) it's SMALL! 61 62CrackLib's dictionary is modified-DAWG compressed with a chunksize of 16 63words (see Crack v5.0 docs (when it comes out) for details) - and then 64the index file is built, with one entry per chunk. 65 66The upshot of all this is that CrackLib can do indexed, binary searches 67in a 1.4 million word dictionary (raw size ~ 15Mb), but the CrackLib 68files (data+index+watermarks) occupy only ~ 7Mb. (45% original size) 69 70It's even efficient over NFS ! 71 72 734) it's MIND-NUMBINGLY THOROUGH! 74 75(is this beginning to read like a B-movie flyer, or what?) 76 77CrackLib makes literally hundreds of tests to determine whether you've 78chosen a bad password. 79 80* It tries to generate words from your username and gecos entry to tries 81to match them against what you've chosen. 82 83* It checks for simplistic patterns. 84 85* It then tries to reverse-engineer your password into a dictionary 86word, and searches for it in your dictionary. 87 88- after all that, it's PROBABLY a safe(-ish) password. 8-) 89 90 91*** Instructions for building CrackLib... 92 93 94STEP 0) Engage your brain. 95 96I'm interested in improving the CrackLib software, doing bugfixes, 97"guessing technique" improvements, and portability enhancements. 98 99I'm NOT interested in unhelpful comments like "well, _my_ operating 100system doesn't come with a dictionary". If it doesn't, either complain 101to your vendor, or GO AND GET a dictionary off the net. 102 103CrackLib is NOT a TOOL. It is not a complete package. It is not 104something you can utilise directly. 105 106It is a resource, an aid, something to enhance the functionality of 107other software. You need to (either) write OR modify other software to 108use it. If you can't do this, then you shouldn't be wasting your time 109with it. 110 111Regarding bugs and portability problems: please try to work them out for 112yourself, and then (please) TELL me about them. This will help me 113improve future versions. 114 115 116STEP 1) Edit the Makefile to set your preferred value of DICTPATH 117 118This it the directory+filename-prefix that your version of CrackLib will 119go hunting for, and it must be visible to all programs on all hosts that 120use CrackLib 121 122Hence, if you want to use a CrackLib binary on a distributed network, 123these files are probably best placed on an NFS server. 124 125Note: You have to specify a FILENAME PREFIX too, eg: 126 127 DICTPATH=/usr/local/lib/pw_dict 128 129which will generate: 130 131 /usr/local/lib/pw_dict.pwd 132 /usr/local/lib/pw_dict.pwi 133 /usr/local/lib/pw_dict.hwm 134 135which are the files that CrackLib needs. 136 137These files are NOT byte-order independent, in fact they are probably 138ARCHITECTURE SPECIFIC, mostly due to speed constraints. If this is a 139problem, I suggest you use: 140 141 DICTPATH=/usr/local/lib/pw_dict.sun4 142 DICTPATH=/usr/local/lib/pw_dict.i386 143 DICTPATH=/usr/local/lib/pw_dict.cray 144 145...etc, and build several sets of files, as appropriate. 146 147 148(Hackers Note: Strictly, only *.pwi and *.hwm should be architecture 149dependent; however, if you build two dictionaries on two different 150platforms, you MAY wind up with different *.pwd files too, due to 151incompatibilities in the std Unix utilities, or from using different 152SOURCEDICTs. 153 154I may try to work this out in the next release. In the mean time, if 155your *.pwd files are EXACTLY identical (use "cmp" to test), you can 156delete the multiple copies and use softlinks instead.) 157 158 159STEP 2) Add to the SOURCEDICT variable, any files continaing extra words 160that you wish CrackLib to use. CrackLib merges all of these files 161together, removes redundant characters, and compresses them. Generally, 162the output file is 40..60% the size of all the input files, combined. 163 164NOTE: THE DEFAULT VALUE OF "SOURCEDICT" CONTAINS "/usr/dict/words" - 165this is a file which can be found on many BSD-type Unix systems, 166containing a list of words, one per line, suitable for use with 167"cracklib". If you do not have such a file, refer to STEP 0. 168 169 170STEP 3) do: 171 172 % make all 173 174then do: 175 176 % make install 177 178which will build the CrackLib dictionary in $DICTPATH. 179 180 181*** NOTE THIS WELL *** 182 183If you supply massive amounts of text to CrackLib to use a a dictionary, 184you must have enough free space available for use by the "sort" command, 185when the dictionary is built. 186 187So: If you do not have (say) about 20Mb free in /usr/tmp (or whatever 188temporary area your "sort" command uses), have a look at the 189"util/mkdict" script. 190 191You can usually tweak the "sort" command to use any large area of disk 192you desire, by use of the "-T" option, and "mkdict" has a hook for this. 193 194 195STEP 4) Wire a call to "FascistCheck()" into your "passwd" program 196 197 198- Left as an exercise for the reader. 199 200 201*** Example of how to invoke CrackLib 202 203Insert a call to the routine FascistCheck, which is defined thusly: 204 205NAME 206 FascistCheck - check a potential password for guessability 207 208 209SYNOPSIS 210 211 char *FascistCheck(char *pw, char *dictpath); 212 213 214DESCRIPTION 215 216 FascistCheck() takes 2 arguments: 217 218 pw - a string continaing the users chosen "potential password" 219 220 dictpath - the full path name + filename prefix of the 221 CrackLib dictionary, specified in the installation Makefile. 222 (If you still haven't sussed, I'm talking about DICTPATH). 223 224 225RETURN VALUE 226 227 FascistCheck() returns the NULL pointer for a good password, 228 or a pointer to a diagnostic string if it is a bad password. 229 230 231BUGS 232 - it can't catch everything. Just most things. 233 234 - it calls getpwuid(getuid()) to look up the user, 235 this MAY affect poorly written programs 236 237 - using more than one pw_dict file, eg: 238 239 char *msg; 240 if (msg = FascistCheck(pw, "onepath") || 241 msg = FascistCheck(pw, "anotherpath")) 242 { 243 printf("Bad Password: because %s\n", msg); 244 } 245 246 ...works, but it's a kludge. AVOID IT IF POSSIBLE. 247 Using just the one dictionary is more efficient, anyway. 248 249 - PWOpen() routines should cope with having more than 1 250 dictionary open at a time. I'll fix this RSN. 251 252 253WORKED EXAMPLE 254 255---- modified extract from BSD distribution - "local_passwd.c" ---- 256 257#ifndef CRACKLIB_DICTPATH /* if possible, get from the same Makefile as CrackLib */ 258#define CRACKLIB_DICTPATH "/usr/local/lib/pw_dict" 259#endif /* see examples on how to import DICTPATH into CRACKLIB_DICTPATH */ 260... 261... 262... 263 for (buf[0] = '\0', tries = 0;;) { 264 p = getpass("New password:"); 265 if (!*p) { 266 (void)printf("Password unchanged.\n"); 267 pw_error(NULL, 0, 0); 268 } 269 270#ifndef CRACKLIB_DICTPATH 271 if (strlen(p) <= 5 && (uid != 0 || ++tries < 2)) { 272 (void)printf("Please enter a longer password.\n"); 273 continue; 274 } 275 for (t = p; *t && islower(*t); ++t); 276 if (!*t && (uid != 0 || ++tries < 2)) { 277 (void)printf("Please don't use an all-lower case password.\nUnusual capitalization, control characters or digits are suggested.\n"); 278 continue; 279 } 280#else 281 { 282 char *msg; 283 if (msg = (char *) FascistCheck(pwbuf, CRACKLIB_DICTPATH)) { 284 printf("Please use a different password.\n"); 285 printf("The one you have chosen is unsuitable because %s.\n", msg); 286 continue; /* go round and round until they get it right */ 287 } 288 } 289#endif /* CRACKLIB_DICTPATH */ 290 291 (void)strcpy(buf, p); 292 if (!strcmp(buf, getpass("Retype new password:"))) 293 break; 294 (void)printf("Mismatch; try again, EOF to quit.\n"); 295 } 296---- end of extract ---- 297