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