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 18 /* Set this to test as a standalone program. */ 19 /* #define DIAGNOSTIC */ 20 21 #ifndef DIAGNOSTIC 22 #include "cvs.h" 23 #else /* ! DIAGNOSTIC */ 24 /* cvs.h won't define this for us */ 25 #define AUTH_CLIENT_SUPPORT 26 #define xmalloc malloc 27 /* Use "gcc -fwritable-strings". */ 28 #include <stdio.h> 29 #include <stdio.h> 30 #include <string.h> 31 #endif /* ! DIAGNOSTIC */ 32 33 #if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT) 34 35 /* Map characters to each other randomly and symmetrically, A <--> B. 36 * 37 * We divide the ASCII character set into 3 domains: control chars (0 38 * thru 31), printing chars (32 through 126), and "meta"-chars (127 39 * through 255). The control chars map _to_ themselves, the printing 40 * chars map _among_ themselves, and the meta chars map _among_ 41 * themselves. Why is this thus? 42 * 43 * No character in any of these domains maps to a character in another 44 * domain, because I'm not sure what characters are legal in 45 * passwords, or what tools people are likely to use to cut and paste 46 * them. It seems prudent not to introduce control or meta chars, 47 * unless the user introduced them first. And having the control 48 * chars all map to themselves insures that newline and 49 * carriage-return are safely handled. 50 */ 51 52 static unsigned char 53 shifts[] = { 54 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 55 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 56 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87, 57 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105, 58 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35, 59 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56, 60 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48, 61 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223, 62 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190, 63 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193, 64 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212, 65 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246, 66 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176, 67 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127, 68 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195, 69 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152 }; 70 71 72 /* SCRAMBLE and DESCRAMBLE work like this: 73 * 74 * scramble(STR) returns SCRM, a scrambled copy of STR. SCRM[0] is a 75 * single letter indicating the scrambling method. As of this 76 * writing, the only legal method is 'A', but check the code for more 77 * up-to-date information. The copy will have been allocated with 78 * malloc(). 79 * 80 * descramble(SCRM) returns STR, again in its own malloc'd space. 81 * descramble() uses SCRM[0] to determine which method of unscrambling 82 * to use. If it does not recognize the method, it dies with error. 83 */ 84 85 /* Return a malloc'd, scrambled version of STR. */ 86 char * 87 scramble (str) 88 char *str; 89 { 90 int i; 91 char *s; 92 93 /* +2 to hold the 'A' prefix that indicates which version of 94 scrambling this is (the first, obviously, since we only do one 95 kind of scrambling so far), and then the '\0' of course. */ 96 s = (char *) xmalloc (strlen (str) + 2); 97 98 /* Scramble (TM) version prefix. */ 99 s[0] = 'A'; 100 strcpy (s + 1, str); 101 102 for (i = 1; s[i]; i++) 103 s[i] = shifts[(unsigned char)(s[i])]; 104 105 return s; 106 } 107 108 /* Decode the string in place. */ 109 char * 110 descramble (str) 111 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 () 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