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