1 /*
2 *
3 * Routines to deal with the VMS User Authorization File:
4 *
5 * get_vms_uaf_record(name,uaf);
6 * validate_vms_user(name,password,uaf);
7 * hash_vms_password(output_buf,input_buf,input_len,username,type,salt);
8 *
9 *
10 */
11
12
13 /*
14 * Includes
15 */
16 #include <vms/fab.h> /* File access block */
17 #include <vms/rab.h> /* Record access block */
18 #include <vms/rmsdef.h> /* RMS return codes */
19 #include <vms/ssdef.h> /* System service return codes */
20 #include <vms/uafdef.h> /* Authorization file records */
21 #include <vms/logdef.h> /* Logical name table seach masks */
22
23 /*
24 * defines
25 */
26 #define RETRY_RLK 2 /* number of retries if record locked */
27 #define SLEEP_RLK 75 /* MS to sleep before retrying a GET */
28 #define RECSIZ 80 /* Maximum length for password string */
29 #define UAFNAME "SYSUAF" /* Name of authorization file */
30 #define UAFSIZE 6 /* Size of above */
31 #define DEFNAME "SYS$SYSTEM:.DAT" /* Default path to authorization file */
32 #define DEFSIZE 15 /* Size of above */
33 #define DEFUSER "DEFAULT " /* "Default user" name */
34
35 #define UAF$_NORMAL 1 /* Normal completion */
36 #define UAF$_INVUSR -2 /* Invalid User Name */
37 #define UAF$_INVPWD -4 /* Invalid Password */
38
39 #define UAF$S_USERNAME 12 /* User Name Size */
40 #define UAF$S_PWD 8 /* Password Size */
41
42 struct descr {int size; char *ptr;}; /* VMS descriptor */
43
44 /*
45 * OWN STORAGE:
46 */
47 static int wakedelta[] = {-10*1000*SLEEP_RLK,-1};
48
49
50 /*
51 *
52 * status = get_vms_uaf_record(name,uaf);
53 *
54 */
get_vms_uaf_record(name,uaf)55 int get_vms_uaf_record(name,uaf)
56 char *name;
57 register struct uaf *uaf;
58 {
59 struct FAB fab;
60 struct RAB rab;
61 unsigned int old_privs[2],new_privs[2];
62 int status;
63 int default_user = 1;
64 register int i;
65 register char *cp,*cp1,*cp2;
66
67 /*
68 * Zero the fab and rab
69 */
70 bzero(&fab,sizeof(fab));
71 bzero(&rab,sizeof(rab));
72 /*
73 * Setup the fab
74 */
75 fab.fab$b_bid = FAB$C_BID;
76 fab.fab$b_bln = sizeof(fab);
77 fab.fab$l_fna = UAFNAME;
78 fab.fab$b_fns = UAFSIZE;
79 fab.fab$l_dna = DEFNAME;
80 fab.fab$b_dns = DEFSIZE;
81 fab.fab$b_dsbmsk = (1<<LOG$C_GROUP) | (1<<LOG$C_PROCESS);
82 fab.fab$b_fac |= FAB$M_GET;
83 fab.fab$b_shr = (FAB$M_GET|FAB$M_PUT|FAB$M_UPD|FAB$M_DEL);
84 fab.fab$b_org = FAB$C_IDX;
85 fab.fab$b_rfm = FAB$C_VAR;
86 /*
87 * setup the rab
88 */
89 rab.rab$b_bid = RAB$C_BID;
90 rab.rab$b_bln = sizeof(rab);
91 rab.rab$b_rac = RAB$C_KEY;
92 rab.rab$l_rop |= RAB$M_NLK;
93 rab.rab$b_mbc = 10;
94 rab.rab$w_usz = sizeof(struct uaf);
95 rab.rab$l_ubf = (char *)uaf;
96 rab.rab$l_kbfpbf.rab$l_kbf = (unsigned long int *)uaf;
97 rab.rab$b_kszpsz.rab$b_ksz = UAF$S_USERNAME;
98 rab.rab$l_fab = &fab;
99 /*
100 * Enable all privileges that we are authorized to have just to
101 * enhance the possibility of accessing the SYSUAF file.
102 */
103 new_privs[0] = -1; new_privs[1] = -1;
104 sys$setprv(1,new_privs,0,old_privs);
105 /*
106 * Open the File and connect the RAB
107 */
108 status = sys$open(&fab);
109 if (!(status & 1)) {
110 if ((status == RMS$_SNE) ||
111 (status == RMS$_SPE) ||
112 (status == RMS$_DME)) {
113 fab.fab$b_shr = 0;
114 status = sys$open(&fab);
115 if (!(status & 1)) goto exit;
116 } else goto exit;
117 }
118 status = sys$connect(&rab);
119 if (!(status & 1)) goto exit;
120 /*
121 * Move the USERNAME to the uaf$t_username field (as a buffer)
122 * uppercaseify it along the way and check it against the
123 * username "DEFAULT" (which is not a real username). Pad
124 * the uaf$t_username field with blanks.
125 */
126 i = UAF$S_USERNAME;
127 cp = name;
128 cp1 = uaf->uaf$t_username;
129 cp2 = DEFUSER;
130 while(--i >= 0) {
131 if (*cp == 0) break;
132 if (*cp != *cp2++) default_user = 0;
133 *cp1++ = ((*cp >= 'a') && (*cp <= 'z')) ?
134 *cp++ + ('A' - 'a') : *cp++;
135 }
136 i++;
137 while(--i >= 0) {
138 *cp1++ = ' ';
139 if (*cp2++ != ' ') default_user = 0;
140 }
141 /*
142 * The "DEFAULT" user is illegal!
143 */
144 if (default_user) {
145 status = UAF$_INVUSR;
146 goto exit;
147 }
148 /*
149 * Look up the User's UAF record
150 */
151 status = get_record(&rab);
152 if (status == RMS$_RNF) status = UAF$_INVUSR;
153
154 exit:
155 /*
156 * Done: close the file, release privileges and return status
157 */
158 sys$disconnect(&rab);
159 sys$close(&fab);
160 sys$setprv(0,new_privs,0,0);
161 sys$setprv(1,old_privs,0,0);
162 return(status);
163 }
164
165
166 /*
167 * Read the record and deal with file locking
168 */
169 static get_record(rab)
170 struct RAB *rab;
171 {
172 int retries = RETRY_RLK;
173 int status;
174
175 /*
176 * Re-try the appropriate number of times
177 */
178 while(1) {
179 /*
180 * Get the record
181 */
182 status = sys$get(rab);
183 /*
184 * If the return code is not "Record Locked" it is either
185 * a success or error that we can't handle, return it.
186 */
187 if (status != RMS$_RLK) break;
188 /*
189 * Record Locked: If retries exceeded, return error
190 */
191 if (--retries < 0) break;
192 /*
193 * Retry: Sleep first
194 */
195 status = sys$schdwk(0,0,wakedelta,0);
196 if (status & 1) sys$hiber();
197 }
198 /*
199 * Done: Return status
200 */
201 return(status);
202 }
203
204
205 /*
206 *
207 * Validate a UserName/Password pair and return the user's UAF record
208 *
209 */
validate_vms_user(name,password,uaf)210 int validate_vms_user(name,password,uaf)
211 char *name;
212 char *password;
213 register struct uaf *uaf;
214 {
215 char password_buf[RECSIZ];
216 char username_buf[UAF$S_USERNAME];
217 char encrypt_buf[UAF$S_PWD];
218 register int i;
219 register char *cp,*cp1;
220
221 /*
222 * Get the User's UAF record
223 */
224 i = get_vms_uaf_record(name,uaf);
225 if (!(i & 1)) return(i);
226 /*
227 * Limit the username to "UAF$S_USERNAME" size while copying and
228 * uppercasifying it. Pad with spaces to "UAF$S_USERNAME" size.
229 */
230 i = UAF$S_USERNAME;
231 cp = name;
232 cp1 = username_buf;
233 while(--i >= 0) {
234 if (*cp == 0) break;
235 *cp1++ = ((*cp >= 'a') && (*cp <= 'z')) ?
236 *cp++ + ('A' - 'a') : *cp++;
237 }
238 i++;
239 while(--i >= 0) *cp1++ = ' ';
240 /*
241 * Limit the password to "RECSIZ" size while copying and
242 * uppercasifying it.
243 */
244 i = RECSIZ;
245 cp = password;
246 cp1 = password_buf;
247 while(--i >= 0) {
248 if (*cp == 0) break;
249 *cp1++ = ((*cp >= 'a') && (*cp <= 'z')) ?
250 *cp++ + ('A' - 'a') : *cp++;
251 }
252 i = (RECSIZ - 1) - i; /* Compute size of password string */
253 /*
254 * Encrypt the password
255 */
256 hash_vms_password(encrypt_buf,password_buf,i,username_buf,
257 uaf->uaf$b_encrypt,uaf->uaf$w_salt);
258 if (bcmp(encrypt_buf,uaf->uaf$l_pwd,UAF$S_PWD) == 0)
259 return(UAF$_NORMAL);
260 else return(UAF$_INVPWD);
261 }
262
263
264 /*
265 *
266 * PASSWORD SMASHING CODE:
267 * The real bit hacking is done in "asm" statements, since
268 * "C" is poorly suited towards quad-word arithmetic!
269 *
270 */
271
272 /*
273 * AUTODIN II CRC Coefficients:
274 */
275 static unsigned long autodin[] = {
276 000000000000,003555610144,007333420310,004666230254,
277 016667040620,015332650764,011554460530,012001270474,
278 035556101440,036003711504,032665521750,031330331614,
279 023331141260,020664751324,024002561170,027557371034
280 };
281
282
283 /*
284 * PURDY Polynomial Coefficients
285 */
286 static long c[] = {
287 -83, -1, /* C1 */
288 -179, -1, /* C2 */
289 -257, -1, /* C3 */
290 -323, -1, /* C4 */
291 -363, -1 /* C5 */
292 };
293
294 /*
295 * Hashing routine
296 */
hash_vms_password(output_buf,input_buf,input_length,username,type,salt)297 hash_vms_password(output_buf,input_buf,input_length,username,type,salt)
298 char *output_buf,*input_buf,*username;
299 unsigned short salt;
300 {
301 register int i;
302 register char *cp;
303
304 /*
305 * Dispatch on encryption type
306 */
307 if (type == 0) {
308 /*
309 * AUTODIN II CRC:
310 */
311 crc(autodin,-1,input_length,input_buf,output_buf);
312 return;
313 } else {
314 /*
315 * PURDY:
316 */
317
318 i = 8;
319 cp = output_buf;
320 while(--i >= 0) *cp++ = 0; /* Init output buffer */
321 /*
322 * Collapse the password into a quadword
323 */
324 collapse(input_length,input_buf,output_buf);
325 /*
326 * Add salt to middle of quadword
327 */
328 *((unsigned short *)(output_buf+3)) += salt;
329 /*
330 * Collapse the username into the quadword
331 */
332 collapse(/*UAF$S_USERNAME*/12,username,output_buf);
333 /*
334 * Compute the PURDY polynomial:
335 */
336 purdy(output_buf,c);
337 }
338 }
339
340 /*
341 * CRC routine:
342 */
crc(table,initial_crc,input_len,input_buf,output_buf)343 static crc(table,initial_crc,input_len,input_buf,output_buf)
344 {
345 asm(" crc *4(ap),8(ap),12(ap),*16(ap)");
346 asm(" clrl r1");
347 asm(" movq r0,*20(ap)");
348 }
349
350 /*
351 * Routine to collapse a string into a quadword:
352 */
collapse(input_len,input_buf,output_buf)353 static collapse(input_len,input_buf,output_buf)
354 register unsigned char *input_buf;
355 register int input_len;
356 register unsigned char *output_buf;
357 {
358 while(input_len > 0) {
359 output_buf[input_len & ~(-8)] += *input_buf++;
360 input_len--;
361 }
362 }
363
364
365 /*
366 *
367 * GROMMY STUFF TO COMPUTE THE PURDY POLYNOMIAL
368 *
369 */
purdy(U,C)370 static purdy(U,C)
371 {
372 /*
373 * This routine computes f(U) = p(U) mod P. Where P is a prime of the form
374 * P = 2^64 - a. The function p is the following polynomial:
375 * X^n0 + X^n1*C1 + X^3*C2 + X^2*C3 + X*C4 + C5
376 * The input U is an unsigned quadword.
377 */
378 asm(" .set A,59"); /* 2^64 -59 is the biggest quad prime */
379
380 asm(" movq *4(ap),-(sp)"); /* Push U */
381 asm(" bsbw PQMOD_R0"); /* Ensure U less than P */
382 asm(" movaq (sp),r4"); /* Maintain a pointer to X*/
383 asm(" pushl 8(ap)");
384 asm(" movl (sp)+,r5"); /* Point to the table of coefficients */
385 asm(" movq (r4),-(sp)");
386 asm(" pushl $((1<<24)-63)");/* n1 */
387 asm(" bsbb PQEXP_R3"); /* X^n1 */
388 asm(" movq (r4),-(sp)");
389 asm(" pushl $((1<<24)-3)");
390 asm(" subl2 $((1<<24)-63),(sp)");/* n0-n1 */
391 asm(" bsbb PQEXP_R3");
392 asm(" movq (r5)+,-(sp)"); /* C1 */
393 asm(" bsbw PQADD_R0"); /* X^(n0 - n1) + C1 */
394 asm(" bsbw PQMUL_R2"); /* X^n0 + X^n1*C1 */
395 asm(" movq (r5)+,-(sp)"); /* C2 */
396 asm(" movq (r4),-(sp)");
397 asm(" bsbw PQMUL_R2"); /* X*C2 */
398 asm(" movq (r5)+,-(sp)"); /* C3 */
399 asm(" bsbw PQADD_R0"); /* X*C2 + C3 */
400 asm(" movq (r4),-(sp)");
401 asm(" bsbb PQMUL_R2"); /* X^2*C2 + X*C3 */
402 asm(" movq (r5)+,-(sp)"); /* C4 */
403 asm(" bsbw PQADD_R0"); /* X^2*C2 + X*C3 + C4 */
404 asm(" movq (r4),-(sp)");
405 asm(" bsbb PQMUL_R2"); /* X^3*C2 + X^2*C3 + C4*X */
406 asm(" movq (r5)+,-(sp)"); /* C5 */
407 asm(" bsbw PQADD_R0"); /* X^3*C2 + X^2*C3 + C4*X + C5 */
408 asm(" bsbw PQADD_R0"); /* Add in the high order terms */
409 asm(" movq (sp)+,*4(ap)"); /* Replace U with f(X) */
410 asm(" movl $1,r0");
411 asm(" ret");
412
413
414 /* Replaces the inputs with U^n mod P where P is of the form */
415 /* P = 2^64 - a. */
416 /* U is a quadword, n is an unsigned longword. */
417
418 asm("PQEXP_R3:");
419 asm(" popr $8"); /* Record return address */
420 asm(" movq $1,-(sp)"); /* Initialize */
421 asm(" movq 8+4(sp),-(sp)");/* Copy U to top of stack for speed */
422 asm(" tstl 8+8(sp)"); /* Only handle n greater than */
423 asm(" beqlu 3f");
424 asm("1: blbc 8+8(sp),2f");
425 asm(" movq (sp),-(sp)"); /* Copy the current power of U */
426 asm(" movq 8+8(sp),-(sp)");/* Multiply with current value */
427 asm(" bsbb PQMUL_R2");
428 asm(" movq (sp)+,8(sp)"); /* Replace current value */
429 asm(" cmpzv $1,$31,8+8(sp),$0");
430 asm(" beqlu 3f");
431 asm("2: movq (sp),-(sp)"); /* Proceed to next power of U */
432 asm(" bsbb PQMUL_R2");
433 asm(" extzv $1,$31,8+8(sp),8+8(sp)");
434 asm(" brb 1b");
435 asm("3: movq 8(sp),8+8+4(sp)");/* Copy the return value */
436 asm(" movaq 8+8+4(sp),sp"); /* Discard the exponent */
437 asm(" jmp (r3)"); /* return */
438
439 /* Replaces the quadword U on the stack with U mod P where P is of the */
440 /* form P = 2^64 - a. */
441 asm(" .set U,0"); /* Low longword of U */
442 asm(" .set V,U+4"); /* High longword of U */
443 asm(" .set Y,U+8"); /* Low longword of Y */
444 asm(" .set Z,Y+4"); /* High longword of Y */
445 asm("PQMOD_R0:");
446 asm(" popr $1"); /* Record return address */
447 asm(" cmpl V(sp),$-1"); /* Replace U with U mod P */
448 asm(" blssu 1f");
449 asm(" cmpl U(sp),$-A");
450 asm(" blssu 1f");
451 asm(" addl2 $A,U(sp)");
452 asm(" adwc $0,V(sp)");
453 asm("1: jmp (r0)"); /* return */
454
455
456 /* Computes the product U*Y mod P where P is of the form
457 * P = 2^64 - a. U, Y are quadwords less than P. The product
458 * replaces U and Y on the stack.
459 *
460 * The product may be formed as the sum of four longword
461 * multiplications which are scaled by powers of 2^32 by evaluating:
462 * 2^64*v*z + 2^32*(v*y + u*z) + u*y
463 * The result is computed such that division by the modulus P
464 * is avoided.
465 */
466 asm("PQMUL_R2:");
467 asm(" popr $2"); /* Record return address */
468 asm(" movl sp,r2"); /* Record initial stack value */
469 asm(" pushl Z(r2)");
470 asm(" pushl V(r2)");
471 asm(" bsbb EMULQ");
472 asm(" bsbb PQMOD_R0");
473 asm(" bsbb PQLSH_R0"); /* Obtain 2^32*v*z */
474 asm(" pushl Y(r2)");
475 asm(" pushl V(r2)");
476 asm(" bsbb EMULQ");
477 asm(" bsbb PQMOD_R0");
478 asm(" pushl Z(r2)");
479 asm(" pushl U(r2)");
480 asm(" bsbb EMULQ");
481 asm(" bsbb PQMOD_R0");
482 asm(" bsbb PQADD_R0"); /* Obtain (v*y + u*z) */
483 asm(" bsbb PQADD_R0"); /* Add in 2^32*v*z */
484 asm(" bsbb PQLSH_R0"); /* Obtain the first two terms */
485 asm(" pushl Y(r2)");
486 asm(" pushl U(r2)");
487 asm(" bsbb EMULQ");
488 asm(" bsbb PQMOD_R0"); /* Obtain the third term: u*y */
489 asm(" bsbb PQADD_R0"); /* Add it in */
490 asm(" movq (sp)+,Y(r2)"); /* Copy the return value */
491 asm(" movaq Y(r2),sp"); /* Point the stack to the return value */
492 asm(" jmp (r1)"); /* return */
493
494
495 /* This routine knows how to multiply two unsigned longwords,
496 * replacing them with the unsigned quadword product on the stack.
497 */
498
499 asm("EMULQ:");
500 asm(" emul 4(sp),8(sp),$0,-(sp)");
501 asm(" clrl -(sp)");
502 asm(" tstl 4+8+4(sp)"); /* Check both longwords to see if we */
503 asm(" bgeq 1f"); /* must compensate for the unsigned
504 bias. */
505 asm(" addl2 4+8+8(sp),(sp)");
506 asm("1: tstl 4+8+8(sp)");
507 asm(" bgeq 2f");
508 asm(" addl2 4+8+4(sp),(sp)");
509 asm("2: addl2 (sp)+,4(sp)"); /* Add in the compensation. */
510 asm(" movq (sp)+,4(sp)"); /* Replace the longwords with their
511 product. */
512 asm(" rsb");
513
514 /*
515 * Computes the product 2^32*U mod P where P is of the form
516 * P = 2^64 - a. U is a quadword less than P. The product replaces
517 * U on the stack.
518 *
519 * This routine is used by PQMUL in the formation of quadword
520 * products in such a way as to avoid division by the modulus P.
521 * The product 2^64*v + 2^32*u is congruent a*v + 2^32*u mod P
522 * (where u, v are longwords).
523 */
524 asm("PQLSH_R0:");
525 asm(" popr $1"); /* Record return address */
526 asm(" pushl V(sp)");
527 asm(" pushl $A");
528 asm(" bsbb EMULQ"); /* Push a*v */
529 asm(" ashq $32,Y(sp),Y(sp)");/* Form Y = 2^32*u */
530 asm(" brb PQADD_R0_1"); /* Return the sum U + Y mod P. */
531
532 /*
533 * Computes the sum U + Y mod P where P is of the form P = 2^64 - a.
534 * U, Y are quadwords less than P. The sum replaces U and Y on
535 * the stack.
536 */
537 asm("PQADD_R0:");
538 asm(" popr $1"); /* Record return address */
539 asm("PQADD_R0_1:");
540 asm(" addl2 U(sp),Y(sp)") /* Add the low longwords */
541 asm(" adwc V(sp),Z(sp)"); /* Add the high longwords with the carry */
542 asm(" bcs 2f"); /* If the result is greater than a quadword */
543 asm(" cmpl Z(sp),$-1");
544 asm(" blssu 3f");
545 asm(" cmpl Y(sp),$-A"); /* or simply greater than or equal to P */
546 asm(" blssu 3f");
547 asm("2: addl2 $A,Y(sp)"); /* we must subtract P.*/
548 asm(" adwc $0,Z(sp)");
549 asm("3: movaq Y(sp),sp"); /* Point the stack to the return value */
550 asm(" jmp (r0)"); /* return */
551 }
552