1/* 2 * $Id: uams_randnum.c,v 1.21 2010-03-30 10:25:49 franklahm Exp $ 3 * 4 * Copyright (c) 1990,1993 Regents of The University of Michigan. 5 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 6 * All Rights Reserved. See COPYRIGHT. 7 */ 8 9#ifdef HAVE_CONFIG_H 10#include "config.h" 11#endif /* HAVE_CONFIG_H */ 12 13#include <stdio.h> 14#include <stdlib.h> 15 16/* STDC check */ 17#if STDC_HEADERS 18#include <string.h> 19#else /* STDC_HEADERS */ 20#ifndef HAVE_STRCHR 21#define strchr index 22#define strrchr index 23#endif /* HAVE_STRCHR */ 24char *strchr (), *strrchr (); 25#ifndef HAVE_MEMCPY 26#define memcpy(d,s,n) bcopy ((s), (d), (n)) 27#define memmove(d,s,n) bcopy ((s), (d), (n)) 28#endif /* ! HAVE_MEMCPY */ 29#endif /* STDC_HEADERS */ 30 31#ifdef HAVE_UNISTD_H 32#include <unistd.h> 33#endif /* HAVE_UNISTD_H */ 34#ifdef HAVE_FCNTL_H 35#include <fcntl.h> 36#endif /* HAVE_FCNTL_H */ 37#include <ctype.h> 38#include <pwd.h> 39#include <sys/stat.h> 40#include <sys/param.h> 41 42#include <atalk/logger.h> 43 44#include <netatalk/endian.h> 45 46#include <atalk/afp.h> 47#include <atalk/uam.h> 48 49 50#include <des.h> 51 52#ifdef USE_CRACKLIB 53#include <crack.h> 54#endif /* USE_CRACKLIB */ 55 56#define PASSWDLEN 8 57 58static C_Block seskey; 59static Key_schedule seskeysched; 60static struct passwd *randpwd; 61static u_int8_t randbuf[8]; 62 63/* hash to a 16-bit number. this will generate completely harmless 64 * warnings on 64-bit machines. */ 65#define randhash(a) (((((unsigned long) a) >> 8) ^ \ 66 ((unsigned long)a)) & 0xffff) 67 68 69/* handle ~/.passwd. courtesy of shirsch@ibm.net. */ 70static int home_passwd(const struct passwd *pwd, 71 const char *path, const int pathlen _U_, 72 unsigned char *passwd, const int len, 73 const int set) 74{ 75 struct stat st; 76 int fd, i; 77 78 if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) { 79 LOG(log_error, logtype_uams, "Failed to open %s", path); 80 return AFPERR_ACCESS; 81 } 82 83 if ( fstat( fd, &st ) < 0 ) 84 goto home_passwd_fail; 85 86 /* If any of these are true, disallow login: 87 * - not a regular file 88 * - gid or uid don't match user 89 * - anyone else has permissions of any sort 90 */ 91 if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) || 92 (pwd->pw_gid != st.st_gid) || 93 (st.st_mode & ( S_IRWXG | S_IRWXO )) ) { 94 LOG(log_info, logtype_uams, "Insecure permissions found for %s.", path); 95 goto home_passwd_fail; 96 } 97 98 /* get the password */ 99 if (set) { 100 if (write(fd, passwd, len) < 0) { 101 LOG(log_error, logtype_uams, "Failed to write to %s", path ); 102 goto home_passwd_fail; 103 } 104 } else { 105 if (read(fd, passwd, len) < 0) { 106 LOG(log_error, logtype_uams, "Failed to read from %s", path ); 107 goto home_passwd_fail; 108 } 109 110 /* get rid of pesky characters */ 111 for (i = 0; i < len; i++) 112 if ((passwd[i] != ' ') && isspace(passwd[i])) 113 passwd[i] = '\0'; 114 } 115 116 close(fd); 117 return AFP_OK; 118 119home_passwd_fail: 120 close(fd); 121 return AFPERR_ACCESS; 122} 123 124 125 126/* 127 * handle /path/afppasswd with an optional key file. we're a lot more 128 * trusting of this file. NOTE: we use our own password entry writing 129 * bits as we want to avoid tromping over global variables. in addition, 130 * we look for a key file and use that if it's there. here are the 131 * formats: 132 * password file: 133 * username:password:last login date:failedcount 134 * 135 * password is just the hex equivalent of either the ASCII password 136 * (if the key file doesn't exist) or the des encrypted password. 137 * 138 * key file: 139 * key (in hex) */ 140#define PASSWD_ILLEGAL '*' 141#define unhex(x) (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A') 142static int afppasswd(const struct passwd *pwd, 143 const char *path, const int pathlen, 144 unsigned char *passwd, int len, 145 const int set) 146{ 147 u_int8_t key[DES_KEY_SZ*2]; 148 char buf[MAXPATHLEN + 1], *p; 149 Key_schedule schedule; 150 FILE *fp; 151 unsigned int i, j; 152 int keyfd = -1, err = 0; 153 off_t pos; 154 155 if ((fp = fopen(path, (set) ? "r+" : "r")) == NULL) { 156 LOG(log_error, logtype_uams, "Failed to open %s", path); 157 return AFPERR_ACCESS; 158 } 159 160 /* open the key file if it exists */ 161 strcpy(buf, path); 162 if (pathlen < (int) sizeof(buf) - 5) { 163 strcat(buf, ".key"); 164 keyfd = open(buf, O_RDONLY); 165 } 166 167 pos = ftell(fp); 168 memset(buf, 0, sizeof(buf)); 169 while (fgets(buf, sizeof(buf), fp)) { 170 if ((p = strchr(buf, ':'))) { 171 if ( strlen(pwd->pw_name) == (p - buf) && 172 strncmp(buf, pwd->pw_name, p - buf) == 0) { 173 p++; 174 if (*p == PASSWD_ILLEGAL) { 175 LOG(log_info, logtype_uams, "invalid password entry for %s", pwd->pw_name); 176 err = AFPERR_ACCESS; 177 goto afppasswd_done; 178 } 179 goto afppasswd_found; 180 } 181 } 182 pos = ftell(fp); 183 memset(buf, 0, sizeof(buf)); 184 } 185 err = AFPERR_PARAM; 186 goto afppasswd_done; 187 188afppasswd_found: 189 if (!set) { 190 /* convert to binary. */ 191 for (i = j = 0; i < sizeof(key); i += 2, j++) 192 p[j] = (unhex(p[i]) << 4) | unhex(p[i + 1]); 193 if (j <= DES_KEY_SZ) 194 memset(p + j, 0, sizeof(key) - j); 195 } 196 197 if (keyfd > -1) { 198 /* read in the hex representation of an 8-byte key */ 199 read(keyfd, key, sizeof(key)); 200 201 /* convert to binary key */ 202 for (i = j = 0; i < strlen((char *) key); i += 2, j++) 203 key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]); 204 if (j <= DES_KEY_SZ) 205 memset(key + j, 0, sizeof(key) - j); 206 key_sched((C_Block *) key, schedule); 207 memset(key, 0, sizeof(key)); 208 209 if (set) { 210 /* NOTE: this takes advantage of the fact that passwd doesn't 211 * get used after this call if it's being set. */ 212 ecb_encrypt((C_Block *) passwd, (C_Block *) passwd, schedule, 213 DES_ENCRYPT); 214 } else { 215 /* decrypt the password */ 216 ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT); 217 } 218 memset(&schedule, 0, sizeof(schedule)); 219 } 220 221 if (set) { 222 const unsigned char hextable[] = "0123456789ABCDEF"; 223 struct flock lock; 224 int fd = fileno(fp); 225 226 /* convert to hex password */ 227 for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) { 228 key[j] = hextable[(passwd[i] & 0xF0) >> 4]; 229 key[j + 1] = hextable[passwd[i] & 0x0F]; 230 } 231 memcpy(p, key, sizeof(key)); 232 233 /* get exclusive access to the user's password entry. we don't 234 * worry so much on reads. in the worse possible case there, the 235 * user will just need to re-enter their password. */ 236 lock.l_type = F_WRLCK; 237 lock.l_start = pos; 238 lock.l_len = 1; 239 lock.l_whence = SEEK_SET; 240 241 fseek(fp, pos, SEEK_SET); 242 fcntl(fd, F_SETLKW, &lock); 243 fwrite(buf, p - buf + sizeof(key), 1, fp); 244 lock.l_type = F_UNLCK; 245 fcntl(fd, F_SETLK, &lock); 246 } else 247 memcpy(passwd, p, len); 248 249 memset(buf, 0, sizeof(buf)); 250 251afppasswd_done: 252 if (keyfd > -1) 253 close(keyfd); 254 fclose(fp); 255 return err; 256} 257 258 259/* this sets the uid. it needs to do slightly different things 260 * depending upon whether or not the password is in ~/.passwd 261 * or in a global location */ 262static int randpass(const struct passwd *pwd, const char *file, 263 unsigned char *passwd, const int len, const int set) 264{ 265 int i; 266 uid_t uid = geteuid(); 267 268 /* Build pathname to user's '.passwd' file */ 269 i = strlen(file); 270 if (*file == '~') { 271 char path[MAXPATHLEN + 1]; 272 273 if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN) 274 return AFPERR_PARAM; 275 276 strcpy(path, pwd->pw_dir ); 277 strcat(path, "/" ); 278 strcat(path, file + 2); 279 if (!uid) 280 seteuid(pwd->pw_uid); /* change ourselves to the user */ 281 i = home_passwd(pwd, path, i, passwd, len, set); 282 if (!uid) 283 seteuid(0); /* change ourselves back to root */ 284 return i; 285 } 286 287 if (i > MAXPATHLEN) 288 return AFPERR_PARAM; 289 290 /* handle afppasswd file. we need to make sure that we're root 291 * when we do this. */ 292 if (uid) 293 seteuid(0); 294 i = afppasswd(pwd, file, i, passwd, len, set); 295 if (uid) 296 seteuid(uid); 297 return i; 298} 299 300/* randnum sends an 8-byte number and uses the user's password to 301 * check against the encrypted reply. */ 302static int rand_login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_, 303 char *ibuf _U_, size_t ibuflen _U_, 304 char *rbuf, size_t *rbuflen) 305{ 306 307 char *passwdfile; 308 u_int16_t sessid; 309 size_t len; 310 int err; 311 312 if (( randpwd = uam_getname(obj, username, ulen)) == NULL ) 313 return AFPERR_NOTAUTH; /* unknown user */ 314 315 LOG(log_info, logtype_uams, "randnum/rand2num login: %s", username); 316 if (uam_checkuser(randpwd) < 0) 317 return AFPERR_NOTAUTH; 318 319 len = UAM_PASSWD_FILENAME; 320 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 321 (void *) &passwdfile, &len) < 0) 322 return AFPERR_PARAM; 323 324 if ((err = randpass(randpwd, passwdfile, seskey, 325 sizeof(seskey), 0)) != AFP_OK) 326 return err; 327 328 /* get a random number */ 329 len = sizeof(randbuf); 330 if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM, 331 (void *) randbuf, &len) < 0) 332 return AFPERR_PARAM; 333 334 /* session id is a hashed version of the obj pointer */ 335 sessid = randhash(obj); 336 memcpy(rbuf, &sessid, sizeof(sessid)); 337 rbuf += sizeof(sessid); 338 *rbuflen = sizeof(sessid); 339 340 /* send the random number off */ 341 memcpy(rbuf, randbuf, sizeof(randbuf)); 342 *rbuflen += sizeof(randbuf); 343 return AFPERR_AUTHCONT; 344} 345 346 347/* check encrypted reply. we actually setup the encryption stuff 348 * here as the first part of randnum and rand2num are identical. */ 349static int randnum_logincont(void *obj, struct passwd **uam_pwd, 350 char *ibuf, size_t ibuflen _U_, 351 char *rbuf _U_, size_t *rbuflen) 352{ 353 u_int16_t sessid; 354 355 *rbuflen = 0; 356 357 memcpy(&sessid, ibuf, sizeof(sessid)); 358 if (sessid != randhash(obj)) 359 return AFPERR_PARAM; 360 361 ibuf += sizeof(sessid); 362 363 /* encrypt. this saves a little space by using the fact that 364 * des can encrypt in-place without side-effects. */ 365 key_sched((C_Block *) seskey, seskeysched); 366 memset(seskey, 0, sizeof(seskey)); 367 ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf, 368 seskeysched, DES_ENCRYPT); 369 memset(&seskeysched, 0, sizeof(seskeysched)); 370 371 /* test against what the client sent */ 372 if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */ 373 memset(randbuf, 0, sizeof(randbuf)); 374 return AFPERR_NOTAUTH; 375 } 376 377 memset(randbuf, 0, sizeof(randbuf)); 378 *uam_pwd = randpwd; 379 return AFP_OK; 380} 381 382 383/* differences from randnum: 384 * 1) each byte of the key is shifted left one bit 385 * 2) client sends the server a 64-bit number. the server encrypts it 386 * and sends it back as part of the reply. 387 */ 388static int rand2num_logincont(void *obj, struct passwd **uam_pwd, 389 char *ibuf, size_t ibuflen _U_, 390 char *rbuf, size_t *rbuflen) 391{ 392 u_int16_t sessid; 393 unsigned int i; 394 395 *rbuflen = 0; 396 397 /* compare session id */ 398 memcpy(&sessid, ibuf, sizeof(sessid)); 399 if (sessid != randhash(obj)) 400 return AFPERR_PARAM; 401 402 ibuf += sizeof(sessid); 403 404 /* shift key elements left one bit */ 405 for (i = 0; i < sizeof(seskey); i++) 406 seskey[i] <<= 1; 407 408 /* encrypt randbuf */ 409 key_sched((C_Block *) seskey, seskeysched); 410 memset(seskey, 0, sizeof(seskey)); 411 ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf, 412 seskeysched, DES_ENCRYPT); 413 414 /* test against client's reply */ 415 if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */ 416 memset(randbuf, 0, sizeof(randbuf)); 417 memset(&seskeysched, 0, sizeof(seskeysched)); 418 return AFPERR_NOTAUTH; 419 } 420 ibuf += sizeof(randbuf); 421 memset(randbuf, 0, sizeof(randbuf)); 422 423 /* encrypt client's challenge and send back */ 424 ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf, 425 seskeysched, DES_ENCRYPT); 426 memset(&seskeysched, 0, sizeof(seskeysched)); 427 *rbuflen = sizeof(randbuf); 428 429 *uam_pwd = randpwd; 430 return AFP_OK; 431} 432 433/* change password -- 434 * NOTE: an FPLogin must already have completed successfully for this 435 * to work. 436 */ 437static int randnum_changepw(void *obj, const char *username _U_, 438 struct passwd *pwd, char *ibuf, 439 size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen _U_) 440{ 441 char *passwdfile; 442 int err; 443 size_t len; 444 445 if (uam_checkuser(pwd) < 0) 446 return AFPERR_ACCESS; 447 448 len = UAM_PASSWD_FILENAME; 449 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 450 (void *) &passwdfile, &len) < 0) 451 return AFPERR_PARAM; 452 453 /* old password is encrypted with new password and new password is 454 * encrypted with old. */ 455 if ((err = randpass(pwd, passwdfile, seskey, 456 sizeof(seskey), 0)) != AFP_OK) 457 return err; 458 459 /* use old passwd to decrypt new passwd */ 460 key_sched((C_Block *) seskey, seskeysched); 461 ibuf += PASSWDLEN; /* new passwd */ 462 ibuf[PASSWDLEN] = '\0'; 463 ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT); 464 465 /* now use new passwd to decrypt old passwd */ 466 key_sched((C_Block *) ibuf, seskeysched); 467 ibuf -= PASSWDLEN; /* old passwd */ 468 ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT); 469 if (memcmp(seskey, ibuf, sizeof(seskey))) 470 err = AFPERR_NOTAUTH; 471 else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0) 472 err = AFPERR_PWDSAME; 473#ifdef USE_CRACKLIB 474 else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB)) 475 err = AFPERR_PWDPOLCY; 476#endif /* USE_CRACKLIB */ 477 478 if (!err) 479 err = randpass(pwd, passwdfile, (unsigned char *)ibuf + PASSWDLEN, sizeof(seskey), 1); 480 481 /* zero out some fields */ 482 memset(&seskeysched, 0, sizeof(seskeysched)); 483 memset(seskey, 0, sizeof(seskey)); 484 memset(ibuf, 0, sizeof(seskey)); /* old passwd */ 485 memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */ 486 487 if (err) 488 return err; 489 490 return( AFP_OK ); 491} 492 493/* randnum login */ 494static int randnum_login(void *obj, struct passwd **uam_pwd, 495 char *ibuf, size_t ibuflen, 496 char *rbuf, size_t *rbuflen) 497{ 498 char *username; 499 size_t len, ulen; 500 501 *rbuflen = 0; 502 503 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, 504 (void *) &username, &ulen) < 0) 505 return AFPERR_MISC; 506 507 if (ibuflen < 2) { 508 return( AFPERR_PARAM ); 509 } 510 511 len = (unsigned char) *ibuf++; 512 ibuflen--; 513 if (!len || len > ibuflen || len > ulen ) { 514 return( AFPERR_PARAM ); 515 } 516 memcpy(username, ibuf, len ); 517 ibuf += len; 518 ibuflen -=len; 519 username[ len ] = '\0'; 520 521 if ((unsigned long) ibuf & 1) { /* pad character */ 522 ++ibuf; 523 ibuflen--; 524 } 525 return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen)); 526} 527 528/* randnum login ext */ 529static int randnum_login_ext(void *obj, char *uname, struct passwd **uam_pwd, 530 char *ibuf, size_t ibuflen, 531 char *rbuf, size_t *rbuflen) 532{ 533 char *username; 534 size_t len, ulen; 535 u_int16_t temp16; 536 537 *rbuflen = 0; 538 539 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, 540 (void *) &username, &ulen) < 0) 541 return AFPERR_MISC; 542 543 if (*uname != 3) 544 return AFPERR_PARAM; 545 uname++; 546 memcpy(&temp16, uname, sizeof(temp16)); 547 len = ntohs(temp16); 548 if (!len || len > ulen ) { 549 return( AFPERR_PARAM ); 550 } 551 memcpy(username, uname +2, len ); 552 username[ len ] = '\0'; 553 return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen)); 554} 555 556static int uam_setup(const char *path) 557{ 558 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Randnum exchange", 559 randnum_login, randnum_logincont, NULL, randnum_login_ext) < 0) 560 return -1; 561 562 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "2-Way Randnum exchange", 563 randnum_login, rand2num_logincont, NULL, randnum_login_ext) < 0) { 564 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange"); 565 return -1; 566 } 567 568 if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange", 569 randnum_changepw) < 0) { 570 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange"); 571 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange"); 572 return -1; 573 } 574 /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange", 575 pam_printer);*/ 576 577 return 0; 578} 579 580static void uam_cleanup(void) 581{ 582 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange"); 583 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange"); 584 uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange"); 585 /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/ 586} 587 588UAM_MODULE_EXPORT struct uam_export uams_randnum = { 589 UAM_MODULE_SERVER, 590 UAM_MODULE_VERSION, 591 uam_setup, uam_cleanup 592}; 593