xref: /original-bsd/usr.bin/uucp/vms/validate.c (revision 1cfaf997)
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  */
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  */
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  */
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  */
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  */
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  */
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