Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Trivially encode strings to protect them from innocent eyes (i.e.,
      3  * inadvertent password compromises, like a network administrator
      4  * who's watching packets for legitimate reasons and accidentally sees
      5  * the password protocol go by).
      6  *
      7  * This is NOT secure encryption.
      8  *
      9  * It would be tempting to encode the password according to username
     10  * and repository, so that the same password would encode to a
     11  * different string when used with different usernames and/or
     12  * repositories.  However, then users would not be able to cut and
     13  * paste passwords around.  They're not supposed to anyway, but we all
     14  * know they will, and there's no reason to make it harder for them if
     15  * we're not trying to provide real security anyway.
     16  */
     17 #include <sys/cdefs.h>
     18 __RCSID("$NetBSD: scramble.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
     19 
     20 /* Set this to test as a standalone program. */
     21 /* #define DIAGNOSTIC */
     22 
     23 #ifndef DIAGNOSTIC
     24 #include "cvs.h"
     25 #else /* ! DIAGNOSTIC */
     26 /* cvs.h won't define this for us */
     27 #define AUTH_CLIENT_SUPPORT
     28 #define xmalloc malloc
     29 /* Use "gcc -fwritable-strings". */
     30 #include <stdio.h>
     31 #include <stdio.h>
     32 #include <string.h>
     33 #endif /* ! DIAGNOSTIC */
     34 
     35 #if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT)
     36 
     37 /* Map characters to each other randomly and symmetrically, A <--> B.
     38  *
     39  * We divide the ASCII character set into 3 domains: control chars (0
     40  * thru 31), printing chars (32 through 126), and "meta"-chars (127
     41  * through 255).  The control chars map _to_ themselves, the printing
     42  * chars map _among_ themselves, and the meta chars map _among_
     43  * themselves.  Why is this thus?
     44  *
     45  * No character in any of these domains maps to a character in another
     46  * domain, because I'm not sure what characters are valid in
     47  * passwords, or what tools people are likely to use to cut and paste
     48  * them.  It seems prudent not to introduce control or meta chars,
     49  * unless the user introduced them first.  And having the control
     50  * chars all map to themselves insures that newline and
     51  * carriage-return are safely handled.
     52  */
     53 
     54 static unsigned char
     55 shifts[] = {
     56     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
     57    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
     58   114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
     59   111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
     60    41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
     61   125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
     62    36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
     63    58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
     64   225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
     65   199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
     66   174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
     67   207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
     68   192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
     69   227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
     70   182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
     71   243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152 };
     72 
     73 
     74 /* SCRAMBLE and DESCRAMBLE work like this:
     75  *
     76  * scramble(STR) returns SCRM, a scrambled copy of STR.  SCRM[0] is a
     77  * single letter indicating the scrambling method.  As of this
     78  * writing, the only valid method is 'A', but check the code for more
     79  * up-to-date information.  The copy will have been allocated with
     80  * xmalloc().
     81  *
     82  * descramble(SCRM) returns STR, again in its own xmalloc'd space.
     83  * descramble() uses SCRM[0] to determine which method of unscrambling
     84  * to use.  If it does not recognize the method, it dies with error.
     85  */
     86 
     87 /* Return a xmalloc'd, scrambled version of STR. */
     88 char *
     89 scramble (char *str)
     90 {
     91     int i;
     92     char *s;
     93 
     94     /* +2 to hold the 'A' prefix that indicates which version of
     95        scrambling this is (the first, obviously, since we only do one
     96        kind of scrambling so far), and then the '\0' of course.  */
     97     s = (char *) xmalloc (strlen (str) + 2);
     98 
     99     /* Scramble (TM) version prefix. */
    100     s[0] = 'A';
    101     strcpy (s + 1, str);
    102 
    103     for (i = 1; s[i]; i++)
    104 	s[i] = shifts[(unsigned char)(s[i])];
    105 
    106     return s;
    107 }
    108 
    109 /* Decode the string in place. */
    110 char *
    111 descramble (char *str)
    112 {
    113     char *s;
    114     int i;
    115 
    116     /* For now we can only handle one kind of scrambling.  In the future
    117        there may be other kinds, and this `if' will become a `switch'.  */
    118     if (str[0] != 'A')
    119 #ifndef DIAGNOSTIC
    120 	error (1, 0, "descramble: unknown scrambling method");
    121 #else  /* DIAGNOSTIC */
    122     {
    123 	fprintf (stderr, "descramble: unknown scrambling method\n", str);
    124 	fflush (stderr);
    125 	exit (EXIT_FAILURE);
    126     }
    127 #endif  /* DIAGNOSTIC */
    128 
    129     /* Method `A' is symmetrical, so scramble again to decrypt. */
    130     s = scramble (str + 1);
    131 
    132     /* Shift the whole string one char to the left, pushing the unwanted
    133        'A' off the left end.  Safe, because s is null-terminated. */
    134     for (i = 0; s[i]; i++)
    135 	s[i] = s[i + 1];
    136 
    137     return s;
    138 }
    139 
    140 #endif /* (AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT) from top of file */
    141 
    142 #ifdef DIAGNOSTIC
    143 int
    144 main( int argc, char **argv )
    145 {
    146     int i;
    147     char *e, *m, biggie[256];
    148 
    149     char *cleartexts[5];
    150     cleartexts[0] = "first";
    151     cleartexts[1] = "the second";
    152     cleartexts[2] = "this is the third";
    153     cleartexts[3] = "$#% !!\\3";
    154     cleartexts[4] = biggie;
    155 
    156     /* Set up the most important test string: */
    157     /* Can't have a real ASCII zero in the string, because we want to
    158        use printf, so we substitute the character zero. */
    159     biggie[0] = '0';
    160     /* The rest of the string gets straight ascending ASCII. */
    161     for (i = 1; i < 256; i++)
    162 	biggie[i] = i;
    163 
    164     /* Test all the strings. */
    165     for (i = 0; i < 5; i++)
    166     {
    167 	printf ("clear%d: %s\n", i, cleartexts[i]);
    168 	e = scramble (cleartexts[i]);
    169 	printf ("scram%d: %s\n", i, e);
    170 	m = descramble (e);
    171 	free (e);
    172 	printf ("clear%d: %s\n\n", i, m);
    173 	free (m);
    174     }
    175 
    176     fflush (stdout);
    177     return 0;
    178 }
    179 #endif /* DIAGNOSTIC */
    180 
    181 /*
    182  * ;;; The Emacs Lisp that did the dirty work ;;;
    183  * (progn
    184  *
    185  *   ;; Helper func.
    186  *   (defun random-elt (lst)
    187  *     (let* ((len (length lst))
    188  *            (rnd (random len)))
    189  *       (nth rnd lst)))
    190  *
    191  *   ;; A list of all characters under 127, each appearing once.
    192  *   (setq non-meta-chars
    193  *         (let ((i 0)
    194  *               (l nil))
    195  *           (while (< i 127)
    196  *             (setq l (cons i l)
    197  *                   i (1+ i)))
    198  *           l))
    199  *
    200  *   ;; A list of all characters 127 and above, each appearing once.
    201  *   (setq meta-chars
    202  *         (let ((i 127)
    203  *               (l nil))
    204  *           (while (< i 256)
    205  *             (setq l (cons i l)
    206  *                   i (1+ i)))
    207  *           l))
    208  *
    209  *   ;; A vector that will hold the chars in a random order.
    210  *   (setq scrambled-chars (make-vector 256 0))
    211  *
    212  *   ;; These characters should map to themselves.
    213  *   (let ((i 0))
    214  *     (while (< i 32)
    215  *       (aset scrambled-chars i i)
    216  *       (setq non-meta-chars (delete i non-meta-chars)
    217  *             i (1+ i))))
    218  *
    219  *   ;; Assign random (but unique) values, within the non-meta chars.
    220  *   (let ((i 32))
    221  *     (while (< i 127)
    222  *       (let ((ch (random-elt non-meta-chars)))
    223  *         (if (= 0 (aref scrambled-chars i))
    224  *             (progn
    225  *               (aset scrambled-chars i ch)
    226  *               (aset scrambled-chars ch i)
    227  *               (setq non-meta-chars (delete ch non-meta-chars)
    228  *                     non-meta-chars (delete i non-meta-chars))))
    229  *         (setq i (1+ i)))))
    230  *
    231  *   ;; Assign random (but unique) values, within the non-meta chars.
    232  *   (let ((i 127))
    233  *     (while (< i 256)
    234  *       (let ((ch (random-elt meta-chars)))
    235  *         (if (= 0 (aref scrambled-chars i))
    236  *             (progn
    237  *               (aset scrambled-chars i ch)
    238  *               (aset scrambled-chars ch i)
    239  *               (setq meta-chars (delete ch meta-chars)
    240  *                     meta-chars (delete i meta-chars))))
    241  *         (setq i (1+ i)))))
    242  *
    243  *   ;; Now use the `scrambled-chars' vector to get your C array.
    244  *   )
    245  */
    246