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: }