]> eyrie.org Git - kerberos/krb5-strength.git/blob - cracklib/rules.c
Change CrackLib tests for system CrackLib
[kerberos/krb5-strength.git] / cracklib / rules.c
1 /*
2  * This program is copyright Alec Muffett 1993. The author disclaims all 
3  * responsibility or liability with respect to it's usage or its effect 
4  * upon hardware or computer systems, and maintains copyright as set out 
5  * in the "LICENCE" document which accompanies distributions of Crack v4.0 
6  * and upwards.
7  */
8
9 /*
10  * Modified as part of the krb5-strength project as follows:
11  *
12  * 2016-08-17  Howard Guo <hguo@suse.com>
13  *   - Double the length of buffers in Mangle to provide enough space to
14  *     handle duplicating rules.
15  * 2007-03-22  Russ Allbery <eagle@eyrie.org>
16  *   - Cap deletion of leading or trailing characters at one more than half
17  *     the length of the password string and no more than five characters.
18  *     This goes with a change to fascist.c that adds rules to delete more
19  *     leading and trailing characters for longer passwords.
20  *   - Additional system includes for other functions.
21  * 2009-10-14  Russ Allbery <eagle@eyrie.org>
22  *   - Simplify Debug() function for how it's actually called.
23  *   - Add ANSI C protototypes for all functions.
24  *   - Tweaks for const cleanliness.
25  *   - Make internal functions static.
26  *   - Remove unused variables.
27  *   - Changed a variable to unsigned to avoid gcc warnings.
28  * 2016-11-06  Russ Allbery <eagle@eyrie.org>
29  *   - Remove unused vers_id to silence GCC warnings.
30  *   - Added GCC __attribute__ marker on Debug() function.
31  */
32
33 #include <stdarg.h>
34
35 #ifndef IN_CRACKLIB
36
37 #include "crack.h"
38
39 #else
40
41 #include "packer.h"
42
43 static void __attribute__((__format__(printf, 2, 3)))
44 Debug(int val, const char *fmt, ...)
45 {
46     if (val < 2) {
47         va_list args;
48
49         va_start(args, fmt);
50         vfprintf(stderr, fmt, args);
51         va_end(args);
52     }
53 }
54
55 #endif
56
57 #include <string.h>
58
59 #define RULE_NOOP       ':'
60 #define RULE_PREPEND    '^'
61 #define RULE_APPEND     '$'
62 #define RULE_REVERSE    'r'
63 #define RULE_UPPERCASE  'u'
64 #define RULE_LOWERCASE  'l'
65 #define RULE_PLURALISE  'p'
66 #define RULE_CAPITALISE 'c'
67 #define RULE_DUPLICATE  'd'
68 #define RULE_REFLECT    'f'
69 #define RULE_SUBSTITUTE 's'
70 #define RULE_MATCH      '/'
71 #define RULE_NOT        '!'
72 #define RULE_LT         '<'
73 #define RULE_GT         '>'
74 #define RULE_EXTRACT    'x'
75 #define RULE_OVERSTRIKE 'o'
76 #define RULE_INSERT     'i'
77 #define RULE_EQUALS     '='
78 #define RULE_PURGE      '@'
79 #define RULE_CLASS      '?'     /* class rule? socialist ethic in cracker? */
80
81 #define RULE_DFIRST     '['
82 #define RULE_DLAST      ']'
83 #define RULE_MFIRST     '('
84 #define RULE_MLAST      ')'
85
86 static int
87 Suffix(const char *myword, const char *suffix)
88 {
89     register int i;
90     register int j;
91     i = strlen(myword);
92     j = strlen(suffix);
93
94     if (i > j)
95     {
96         return (STRCMP((myword + i - j), suffix));
97     } else
98     {
99         return (-1);
100     }
101 }
102
103 /* return a pointer to a reversal */
104 char *
105 Reverse(const char *str)
106 {
107     register int i;
108     register int j;
109     static char area[STRINGSIZE];
110     j = i = strlen(str);
111     while (*str)
112     {
113         area[--i] = *str++;
114     }
115     area[j] = '\0';
116     return (area);
117 }
118
119 /* return a pointer to an uppercase */
120 static char *
121 Uppercase(const char *str)
122 {
123     register char *ptr;
124     static char area[STRINGSIZE];
125     ptr = area;
126     while (*str)
127     {
128         *(ptr++) = CRACK_TOUPPER(*str);
129         str++;
130     }
131     *ptr = '\0';
132
133     return (area);
134 }
135
136 /* return a pointer to an lowercase */
137 char *
138 Lowercase(const char *str)
139 {
140     register char *ptr;
141     static char area[STRINGSIZE];
142     ptr = area;
143     while (*str)
144     {
145         *(ptr++) = CRACK_TOLOWER(*str);
146         str++;
147     }
148     *ptr = '\0';
149
150     return (area);
151 }
152
153 /* return a pointer to an capitalised */
154 static char *
155 Capitalise(const char *str)
156 {
157     register char *ptr;
158     static char area[STRINGSIZE];
159     ptr = area;
160
161     while (*str)
162     {
163         *(ptr++) = CRACK_TOLOWER(*str);
164         str++;
165     }
166
167     *ptr = '\0';
168     area[0] = CRACK_TOUPPER(area[0]);
169     return (area);
170 }
171
172 /* returns a pointer to a plural */
173 static char *
174 Pluralise(const char *string)
175 {
176     register int length;
177     static char area[STRINGSIZE];
178     length = strlen(string);
179     strcpy(area, string);
180
181     if (!Suffix(string, "ch") ||
182         !Suffix(string, "ex") ||
183         !Suffix(string, "ix") ||
184         !Suffix(string, "sh") ||
185         !Suffix(string, "ss"))
186     {
187         /* bench -> benches */
188         strcat(area, "es");
189     } else if (length > 2 && string[length - 1] == 'y')
190     {
191         if (strchr("aeiou", string[length - 2]))
192         {
193             /* alloy -> alloys */
194             strcat(area, "s");
195         } else
196         {
197             /* gully -> gullies */
198             strcpy(area + length - 1, "ies");
199         }
200     } else if (string[length - 1] == 's')
201     {
202         /* bias -> biases */
203         strcat(area, "es");
204     } else
205     {
206         /* catchall */
207         strcat(area, "s");
208     }
209
210     return (area);
211 }
212
213 /* returns pointer to a swapped about copy */
214 static char *
215 Substitute(const char *string, char old, char new)
216 {
217     register char *ptr;
218     static char area[STRINGSIZE];
219     ptr = area;
220     while (*string)
221     {
222         *(ptr++) = (*string == old ? new : *string);
223         string++;
224     }
225     *ptr = '\0';
226     return (area);
227 }
228
229 /* returns pointer to a purged copy */
230 static char *
231 Purge(const char *string, char target)
232 {
233     register char *ptr;
234     static char area[STRINGSIZE];
235     ptr = area;
236     while (*string)
237     {
238         if (*string != target)
239         {
240             *(ptr++) = *string;
241         }
242         string++;
243     }
244     *ptr = '\0';
245     return (area);
246 }
247 /* -------- CHARACTER CLASSES START HERE -------- */
248
249 /*
250  * this function takes two inputs, a class identifier and a character, and
251  * returns non-null if the given character is a member of the class, based
252  * upon restrictions set out below
253  */
254
255 static int
256 MatchClass(char class, char input)
257 {
258     register char c;
259     register int retval;
260     retval = 0;
261
262     switch (class)
263     {
264         /* ESCAPE */
265
266     case '?':                   /* ?? -> ? */
267         if (input == '?')
268         {
269             retval = 1;
270         }
271         break;
272
273         /* ILLOGICAL GROUPINGS (ie: not in ctype.h) */
274
275     case 'V':
276     case 'v':                   /* vowels */
277         c = CRACK_TOLOWER(input);
278         if (strchr("aeiou", c))
279         {
280             retval = 1;
281         }
282         break;
283
284     case 'C':
285     case 'c':                   /* consonants */
286         c = CRACK_TOLOWER(input);
287         if (strchr("bcdfghjklmnpqrstvwxyz", c))
288         {
289             retval = 1;
290         }
291         break;
292
293     case 'W':
294     case 'w':                   /* whitespace */
295         if (strchr("\t ", input))
296         {
297             retval = 1;
298         }
299         break;
300
301     case 'P':
302     case 'p':                   /* punctuation */
303         if (strchr(".`,:;'!?\"", input))
304         {
305             retval = 1;
306         }
307         break;
308
309     case 'S':
310     case 's':                   /* symbols */
311         if (strchr("$%%^&*()-_+=|\\[]{}#@/~", input))
312         {
313             retval = 1;
314         }
315         break;
316
317         /* LOGICAL GROUPINGS */
318
319     case 'L':
320     case 'l':                   /* lowercase */
321         if (islower(input))
322         {
323             retval = 1;
324         }
325         break;
326
327     case 'U':
328     case 'u':                   /* uppercase */
329         if (isupper(input))
330         {
331             retval = 1;
332         }
333         break;
334
335     case 'A':
336     case 'a':                   /* alphabetic */
337         if (isalpha(input))
338         {
339             retval = 1;
340         }
341         break;
342
343     case 'X':
344     case 'x':                   /* alphanumeric */
345         if (isalnum(input))
346         {
347             retval = 1;
348         }
349         break;
350
351     case 'D':
352     case 'd':                   /* digits */
353         if (isdigit(input))
354         {
355             retval = 1;
356         }
357         break;
358
359     default:
360         Debug(1, "MatchClass: unknown class %c\n", class);
361         return (0);
362         break;
363     }
364
365     if (isupper(class))
366     {
367         return (!retval);
368     }
369     return (retval);
370 }
371
372 static const char *
373 PolyStrchr(const char *string, char class)
374 {
375     while (*string)
376     {
377         if (MatchClass(class, *string))
378         {
379             return (string);
380         }
381         string++;
382     }
383     return ((char *) 0);
384 }
385
386 /* returns pointer to a swapped about copy */
387 static char *
388 PolySubst(const char *string, char class, char new)
389 {
390     register char *ptr;
391     static char area[STRINGSIZE];
392     ptr = area;
393     while (*string)
394     {
395         *(ptr++) = (MatchClass(class, *string) ? new : *string);
396         string++;
397     }
398     *ptr = '\0';
399     return (area);
400 }
401
402 /* returns pointer to a purged copy */
403 static char *
404 PolyPurge(const char *string, const char class)
405 {
406     register char *ptr;
407     static char area[STRINGSIZE];
408     ptr = area;
409     while (*string)
410     {
411         if (!MatchClass(class, *string))
412         {
413             *(ptr++) = *string;
414         }
415         string++;
416     }
417     *ptr = '\0';
418     return (area);
419 }
420 /* -------- BACK TO NORMALITY -------- */
421
422 static int
423 Char2Int(char character)
424 {
425     if (isdigit(character))
426     {
427         return (character - '0');
428     } else if (islower(character))
429     {
430         return (character - 'a' + 10);
431     } else if (isupper(character))
432     {
433         return (character - 'A' + 10);
434     }
435     return (-1);
436 }
437
438 /* returns a pointer to a controlled Mangle */
439 char *
440 Mangle(const char *input, const char *control)
441 {
442     int limit, min_to_shift;
443     register int j;
444     const char *ptr;
445     static char area[STRINGSIZE * 2] = "";
446     char area2[STRINGSIZE * 2] = "";
447     strcpy(area, input);
448
449     j = strlen(input);
450     if (j % 2 == 0)
451     { 
452         min_to_shift = (j + 1) / 2;
453     } else
454     {
455         min_to_shift = j / 2;
456     }
457     min_to_shift++;
458     if (min_to_shift > 5)
459     {
460         min_to_shift = 5;
461     }
462     
463     for (ptr = control; *ptr; ptr++)
464     {
465         switch (*ptr)
466         {
467         case RULE_NOOP:
468             break;
469         case RULE_REVERSE:
470             strcpy(area, Reverse(area));
471             break;
472         case RULE_UPPERCASE:
473             strcpy(area, Uppercase(area));
474             break;
475         case RULE_LOWERCASE:
476             strcpy(area, Lowercase(area));
477             break;
478         case RULE_CAPITALISE:
479             strcpy(area, Capitalise(area));
480             break;
481         case RULE_PLURALISE:
482             strcpy(area, Pluralise(area));
483             break;
484         case RULE_REFLECT:
485             strcat(area, Reverse(area));
486             break;
487         case RULE_DUPLICATE:
488             strcpy(area2, area);
489             strcat(area, area2);
490             break;
491         case RULE_GT:
492             if (!ptr[1])
493             {
494                 Debug(1, "Mangle: '>' missing argument in '%s'\n", control);
495                 return ((char *) 0);
496             } else
497             {
498                 limit = Char2Int(*(++ptr));
499                 if (limit < 0)
500                 {
501                     Debug(1, "Mangle: '>' weird argument in '%s'\n", control);
502                     return ((char *) 0);
503                 }
504                 if (strlen(area) <= (size_t) limit)
505                 {
506                     return ((char *) 0);
507                 }
508             }
509             break;
510         case RULE_LT:
511             if (!ptr[1])
512             {
513                 Debug(1, "Mangle: '<' missing argument in '%s'\n", control);
514                 return ((char *) 0);
515             } else
516             {
517                 limit = Char2Int(*(++ptr));
518                 if (limit < 0)
519                 {
520                     Debug(1, "Mangle: '<' weird argument in '%s'\n", control);
521                     return ((char *) 0);
522                 }
523                 if (strlen(area) >= (size_t) limit)
524                 {
525                     return ((char *) 0);
526                 }
527             }
528             break;
529         case RULE_PREPEND:
530             if (!ptr[1])
531             {
532                 Debug(1, "Mangle: prepend missing argument in '%s'\n", control);
533                 return ((char *) 0);
534             } else
535             {
536                 area2[0] = *(++ptr);
537                 strcpy(area2 + 1, area);
538                 strcpy(area, area2);
539             }
540             break;
541         case RULE_APPEND:
542             if (!ptr[1])
543             {
544                 Debug(1, "Mangle: append missing argument in '%s'\n", control);
545                 return ((char *) 0);
546             } else
547             {
548                 register char *string;
549                 string = area;
550                 while (*(string++));
551                 string[-1] = *(++ptr);
552                 *string = '\0';
553             }
554             break;
555         case RULE_EXTRACT:
556             if (!ptr[1] || !ptr[2])
557             {
558                 Debug(1, "Mangle: extract missing argument in '%s'\n", control);
559                 return ((char *) 0);
560             } else
561             {
562                 register int i;
563                 int start;
564                 int length;
565                 start = Char2Int(*(++ptr));
566                 length = Char2Int(*(++ptr));
567                 if (start < 0 || length < 0)
568                 {
569                     Debug(1, "Mangle: extract: weird argument in '%s'\n", control);
570                     return ((char *) 0);
571                 }
572                 strcpy(area2, area);
573                 for (i = 0; length-- && area2[start + i]; i++)
574                 {
575                     area[i] = area2[start + i];
576                 }
577                 /* cant use strncpy() - no trailing NUL */
578                 area[i] = '\0';
579             }
580             break;
581         case RULE_OVERSTRIKE:
582             if (!ptr[1] || !ptr[2])
583             {
584                 Debug(1, "Mangle: overstrike missing argument in '%s'\n", control);
585                 return ((char *) 0);
586             } else
587             {
588                 register int i;
589                 i = Char2Int(*(++ptr));
590                 if (i < 0)
591                 {
592                     Debug(1, "Mangle: overstrike weird argument in '%s'\n",
593                           control);
594                     return ((char *) 0);
595                 } else
596                 {
597                     ++ptr;
598                     if (area[i])
599                     {
600                         area[i] = *ptr;
601                     }
602                 }
603             }
604             break;
605         case RULE_INSERT:
606             if (!ptr[1] || !ptr[2])
607             {
608                 Debug(1, "Mangle: insert missing argument in '%s'\n", control);
609                 return ((char *) 0);
610             } else
611             {
612                 register int i;
613                 register char *p1;
614                 register char *p2;
615                 i = Char2Int(*(++ptr));
616                 if (i < 0)
617                 {
618                     Debug(1, "Mangle: insert weird argument in '%s'\n",
619                           control);
620                     return ((char *) 0);
621                 }
622                 p1 = area;
623                 p2 = area2;
624                 while (i && *p1)
625                 {
626                     i--;
627                     *(p2++) = *(p1++);
628                 }
629                 *(p2++) = *(++ptr);
630                 strcpy(p2, p1);
631                 strcpy(area, area2);
632             }
633             break;
634             /* THE FOLLOWING RULES REQUIRE CLASS MATCHING */
635
636         case RULE_PURGE:        /* @x or @?c */
637             if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
638             {
639                 Debug(1, "Mangle: delete missing arguments in '%s'\n", control);
640                 return ((char *) 0);
641             } else if (ptr[1] != RULE_CLASS)
642             {
643                 strcpy(area, Purge(area, *(++ptr)));
644             } else
645             {
646                 strcpy(area, PolyPurge(area, ptr[2]));
647                 ptr += 2;
648             }
649             break;
650         case RULE_SUBSTITUTE:   /* sxy || s?cy */
651             if (!ptr[1] || !ptr[2] || (ptr[1] == RULE_CLASS && !ptr[3]))
652             {
653                 Debug(1, "Mangle: subst missing argument in '%s'\n", control);
654                 return ((char *) 0);
655             } else if (ptr[1] != RULE_CLASS)
656             {
657                 strcpy(area, Substitute(area, ptr[1], ptr[2]));
658                 ptr += 2;
659             } else
660             {
661                 strcpy(area, PolySubst(area, ptr[2], ptr[3]));
662                 ptr += 3;
663             }
664             break;
665         case RULE_MATCH:        /* /x || /?c */
666             if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
667             {
668                 Debug(1, "Mangle: '/' missing argument in '%s'\n", control);
669                 return ((char *) 0);
670             } else if (ptr[1] != RULE_CLASS)
671             {
672                 if (!strchr(area, *(++ptr)))
673                 {
674                     return ((char *) 0);
675                 }
676             } else
677             {
678                 if (!PolyStrchr(area, ptr[2]))
679                 {
680                     return ((char *) 0);
681                 }
682                 ptr += 2;
683             }
684             break;
685         case RULE_NOT:          /* !x || !?c */
686             if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
687             {
688                 Debug(1, "Mangle: '!' missing argument in '%s'\n", control);
689                 return ((char *) 0);
690             } else if (ptr[1] != RULE_CLASS)
691             {
692                 if (strchr(area, *(++ptr)))
693                 {
694                     return ((char *) 0);
695                 }
696             } else
697             {
698                 if (PolyStrchr(area, ptr[2]))
699                 {
700                     return ((char *) 0);
701                 }
702                 ptr += 2;
703             }
704             break;
705             /*
706              * alternative use for a boomerang, number 1: a standard throwing
707              * boomerang is an ideal thing to use to tuck the sheets under
708              * the mattress when making your bed.  The streamlined shape of
709              * the boomerang allows it to slip easily 'twixt mattress and
710              * bedframe, and it's curve makes it very easy to hook sheets
711              * into the gap.
712              */
713
714         case RULE_EQUALS:       /* =nx || =n?c */
715             if (!ptr[1] || !ptr[2] || (ptr[2] == RULE_CLASS && !ptr[3]))
716             {
717                 Debug(1, "Mangle: '=' missing argument in '%s'\n", control);
718                 return ((char *) 0);
719             } else
720             {
721                 register int i;
722                 if ((i = Char2Int(ptr[1])) < 0)
723                 {
724                     Debug(1, "Mangle: '=' weird argument in '%s'\n", control);
725                     return ((char *) 0);
726                 }
727                 if (ptr[2] != RULE_CLASS)
728                 {
729                     ptr += 2;
730                     if (area[i] != *ptr)
731                     {
732                         return ((char *) 0);
733                     }
734                 } else
735                 {
736                     ptr += 3;
737                     if (!MatchClass(*ptr, area[i]))
738                     {
739                         return ((char *) 0);
740                     }
741                 }
742             }
743             break;
744
745         case RULE_DFIRST:
746             if (area[0] && strlen(area) > (size_t) min_to_shift)
747             {
748                 register int i;
749                 for (i = 1; area[i]; i++)
750                 {
751                     area[i - 1] = area[i];
752                 }
753                 area[i - 1] = '\0';
754             }
755             break;
756
757         case RULE_DLAST:
758             if (area[0] && strlen(area) > (size_t) min_to_shift)
759             {
760                 register int i;
761                 for (i = 1; area[i]; i++);
762                 area[i - 1] = '\0';
763             }
764             break;
765
766         case RULE_MFIRST:
767             if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
768             {
769                 Debug(1, "Mangle: '(' missing argument in '%s'\n", control);
770                 return ((char *) 0);
771             } else
772             {
773                 if (ptr[1] != RULE_CLASS)
774                 {
775                     ptr++;
776                     if (area[0] != *ptr)
777                     {
778                         return ((char *) 0);
779                     }
780                 } else
781                 {
782                     ptr += 2;
783                     if (!MatchClass(*ptr, area[0]))
784                     {
785                         return ((char *) 0);
786                     }
787                 }
788             }
789         case RULE_MLAST:
790             if (!ptr[1] || (ptr[1] == RULE_CLASS && !ptr[2]))
791             {
792                 Debug(1, "Mangle: ')' missing argument in '%s'\n", control);
793                 return ((char *) 0);
794             } else
795             {
796                 register int i;
797
798                 for (i = 0; area[i]; i++);
799
800                 if (i > 0)
801                 {
802                     i--;
803                 } else
804                 {
805                     return ((char *) 0);
806                 }
807
808                 if (ptr[1] != RULE_CLASS)
809                 {
810                     ptr++;
811                     if (area[i] != *ptr)
812                     {
813                         return ((char *) 0);
814                     }
815                 } else
816                 {
817                     ptr += 2;
818                     if (!MatchClass(*ptr, area[i]))
819                     {
820                         return ((char *) 0);
821                     }
822                 }
823             }
824
825         default:
826             Debug(1, "Mangle: unknown command %c in %s\n", *ptr, control);
827             return ((char *) 0);
828             break;
829         }
830     }
831     if (!area[0])               /* have we deweted de poor widdle fing away? */
832     {
833         return ((char *) 0);
834     }
835     return (area);
836 }
837
838 int
839 PMatch(const char *control, const char *string)
840 {
841     while (*string && *control)
842     {
843         if (!MatchClass(*control, *string))
844         {
845             return(0);
846         }
847
848         string++;
849         control++;
850     }
851
852     if (*string || *control)
853     {
854         return(0);
855     }
856
857     return(1);
858 }