pam_unix.c revision 94148
1/*- 2 * Copyright 1998 Juniper Networks, Inc. 3 * All rights reserved. 4 * Copyright (c) 2002 Networks Associates Technology, Inc. 5 * All rights reserved. 6 * 7 * Portions of this software was developed for the FreeBSD Project by 8 * ThinkSec AS and NAI Labs, the Security Research Division of Network 9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10 * ("CBOSS"), as part of the DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. The name of the author may not be used to endorse or promote 21 * products derived from this software without specific prior written 22 * permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#include <sys/cdefs.h> 38__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_unix/pam_unix.c 94148 2002-04-07 20:43:27Z des $"); 39 40#include <sys/param.h> 41#include <sys/socket.h> 42#include <sys/time.h> 43#include <netinet/in.h> 44#include <arpa/inet.h> 45 46#ifdef YP 47#include <rpc/rpc.h> 48#include <rpcsvc/yp_prot.h> 49#include <rpcsvc/ypclnt.h> 50#include <rpcsvc/yppasswd.h> 51#endif 52 53#include <login_cap.h> 54#include <netdb.h> 55#include <pwd.h> 56#include <stdlib.h> 57#include <string.h> 58#include <stdio.h> 59#include <syslog.h> 60#include <unistd.h> 61 62#include <pw_copy.h> 63#include <pw_util.h> 64 65#ifdef YP 66#include <pw_yp.h> 67#include "yppasswd_private.h" 68#endif 69 70#define PAM_SM_AUTH 71#define PAM_SM_ACCOUNT 72#define PAM_SM_SESSION 73#define PAM_SM_PASSWORD 74 75#include <security/pam_appl.h> 76#include <security/pam_modules.h> 77#include <security/pam_mod_misc.h> 78 79#define PASSWORD_HASH "md5" 80#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ 81#define SALTSIZE 32 82 83static void makesalt(char []); 84 85static char password_hash[] = PASSWORD_HASH; 86static char colon[] = ":"; 87 88enum { 89 PAM_OPT_AUTH_AS_SELF = PAM_OPT_STD_MAX, 90 PAM_OPT_NULLOK, 91 PAM_OPT_LOCAL_PASS, 92 PAM_OPT_NIS_PASS 93}; 94 95static struct opttab other_options[] = { 96 { "auth_as_self", PAM_OPT_AUTH_AS_SELF }, 97 { "nullok", PAM_OPT_NULLOK }, 98 { "local_pass", PAM_OPT_LOCAL_PASS }, 99 { "nis_pass", PAM_OPT_NIS_PASS }, 100 { NULL, 0 } 101}; 102 103#ifdef YP 104int pam_use_yp = 0; 105int yp_errno = YP_TRUE; 106#endif 107 108char *tempname = NULL; 109static int local_passwd(const char *user, const char *pass); 110#ifdef YP 111static int yp_passwd(const char *user, const char *pass); 112#endif 113 114/* 115 * authentication management 116 */ 117PAM_EXTERN int 118pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc, const char **argv) 119{ 120 login_cap_t *lc; 121 struct options options; 122 struct passwd *pwd; 123 int retval; 124 const char *pass, *user; 125 char *encrypted, *password_prompt; 126 127 pam_std_option(&options, other_options, argc, argv); 128 129 PAM_LOG("Options processed"); 130 131 if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) 132 pwd = getpwnam(getlogin()); 133 else { 134 retval = pam_get_user(pamh, &user, NULL); 135 if (retval != PAM_SUCCESS) 136 PAM_RETURN(retval); 137 pwd = getpwnam(user); 138 } 139 140 PAM_LOG("Got user: %s", user); 141 142 if (pwd != NULL) { 143 144 PAM_LOG("Doing real authentication"); 145 146 if (pwd->pw_passwd[0] == '\0' 147 && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { 148 /* 149 * No password case. XXX Are we giving too much away 150 * by not prompting for a password? 151 */ 152 PAM_LOG("No password, and null password OK"); 153 PAM_RETURN(PAM_SUCCESS); 154 } 155 else { 156 lc = login_getpwclass(pwd); 157 password_prompt = login_getcapstr(lc, "passwd_prompt", 158 NULL, NULL); 159 retval = pam_get_authtok(pamh, PAM_AUTHTOK, 160 &pass, password_prompt); 161 login_close(lc); 162 if (retval != PAM_SUCCESS) 163 PAM_RETURN(retval); 164 PAM_LOG("Got password"); 165 } 166 encrypted = crypt(pass, pwd->pw_passwd); 167 if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') 168 encrypted = colon; 169 170 PAM_LOG("Encrypted password 1 is: %s", encrypted); 171 PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd); 172 173 retval = strcmp(encrypted, pwd->pw_passwd) == 0 ? 174 PAM_SUCCESS : PAM_AUTH_ERR; 175 } 176 else { 177 178 PAM_LOG("Doing dummy authentication"); 179 180 /* 181 * User unknown. 182 * Encrypt a dummy password so as to not give away too much. 183 */ 184 lc = login_getclass(NULL); 185 password_prompt = login_getcapstr(lc, "passwd_prompt", 186 NULL, NULL); 187 retval = pam_get_authtok(pamh, 188 PAM_AUTHTOK, &pass, password_prompt); 189 login_close(lc); 190 if (retval != PAM_SUCCESS) 191 PAM_RETURN(retval); 192 PAM_LOG("Got password"); 193 crypt(pass, "xx"); 194 retval = PAM_AUTH_ERR; 195 } 196 197 /* 198 * The PAM infrastructure will obliterate the cleartext 199 * password before returning to the application. 200 */ 201 if (retval != PAM_SUCCESS) 202 PAM_VERBOSE_ERROR("UNIX authentication refused"); 203 204 PAM_RETURN(retval); 205} 206 207PAM_EXTERN int 208pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 209{ 210 struct options options; 211 212 pam_std_option(&options, other_options, argc, argv); 213 214 PAM_LOG("Options processed"); 215 216 PAM_RETURN(PAM_SUCCESS); 217} 218 219/* 220 * account management 221 */ 222PAM_EXTERN int 223pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, int argc, const char **argv) 224{ 225 struct addrinfo hints, *res; 226 struct options options; 227 struct passwd *pwd; 228 struct timeval tp; 229 login_cap_t *lc; 230 time_t warntime; 231 int retval; 232 const char *rhost, *tty, *user; 233 char rhostip[MAXHOSTNAMELEN]; 234 235 pam_std_option(&options, other_options, argc, argv); 236 237 PAM_LOG("Options processed"); 238 239 retval = pam_get_user(pamh, &user, NULL); 240 if (retval != PAM_SUCCESS) 241 PAM_RETURN(retval); 242 243 if (user == NULL || (pwd = getpwnam(user)) == NULL) 244 PAM_RETURN(PAM_SERVICE_ERR); 245 246 PAM_LOG("Got user: %s", user); 247 248 retval = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost); 249 if (retval != PAM_SUCCESS) 250 PAM_RETURN(retval); 251 252 retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty); 253 if (retval != PAM_SUCCESS) 254 PAM_RETURN(retval); 255 256 if (*pwd->pw_passwd == '\0' && 257 (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0) 258 return (PAM_NEW_AUTHTOK_REQD); 259 260 lc = login_getpwclass(pwd); 261 if (lc == NULL) { 262 PAM_LOG("Unable to get login class for user %s", user); 263 return (PAM_SERVICE_ERR); 264 } 265 266 PAM_LOG("Got login_cap"); 267 268 if (pwd->pw_change || pwd->pw_expire) 269 gettimeofday(&tp, NULL); 270 271 /* 272 * Check pw_expire before pw_change - no point in letting the 273 * user change the password on an expired account. 274 */ 275 276 if (pwd->pw_expire) { 277 warntime = login_getcaptime(lc, "warnexpire", 278 DEFAULT_WARN, DEFAULT_WARN); 279 if (tp.tv_sec >= pwd->pw_expire) { 280 login_close(lc); 281 PAM_RETURN(PAM_ACCT_EXPIRED); 282 } else if (pwd->pw_expire - tp.tv_sec < warntime && 283 (flags & PAM_SILENT) == 0) { 284 pam_error(pamh, "Warning: your account expires on %s", 285 ctime(&pwd->pw_expire)); 286 } 287 } 288 289 retval = PAM_SUCCESS; 290 if (pwd->pw_change) { 291 warntime = login_getcaptime(lc, "warnpassword", 292 DEFAULT_WARN, DEFAULT_WARN); 293 if (tp.tv_sec >= pwd->pw_change) { 294 retval = PAM_NEW_AUTHTOK_REQD; 295 } else if (pwd->pw_change - tp.tv_sec < warntime && 296 (flags & PAM_SILENT) == 0) { 297 pam_error(pamh, "Warning: your password expires on %s", 298 ctime(&pwd->pw_change)); 299 } 300 } 301 302 /* 303 * From here on, we must leave retval untouched (unless we 304 * know we're going to fail), because we need to remember 305 * whether we're supposed to return PAM_SUCCESS or 306 * PAM_NEW_AUTHTOK_REQD. 307 */ 308 309 if (rhost) { 310 memset(&hints, 0, sizeof(hints)); 311 hints.ai_family = AF_UNSPEC; 312 if (getaddrinfo(rhost, NULL, &hints, &res) == 0) { 313 getnameinfo(res->ai_addr, res->ai_addrlen, 314 rhostip, sizeof(rhostip), NULL, 0, 315 NI_NUMERICHOST|NI_WITHSCOPEID); 316 } 317 if (res != NULL) 318 freeaddrinfo(res); 319 } 320 321 /* 322 * Check host / tty / time-of-day restrictions 323 */ 324 325 if (!auth_hostok(lc, rhost, rhostip) || 326 !auth_ttyok(lc, tty) || 327 !auth_timeok(lc, time(NULL))) 328 retval = PAM_AUTH_ERR; 329 330 login_close(lc); 331 332 PAM_RETURN(retval); 333} 334 335/* 336 * session management 337 * 338 * logging only 339 */ 340PAM_EXTERN int 341pam_sm_open_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 342{ 343 struct options options; 344 345 pam_std_option(&options, other_options, argc, argv); 346 347 PAM_LOG("Options processed"); 348 349 PAM_RETURN(PAM_SUCCESS); 350} 351 352PAM_EXTERN int 353pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 354{ 355 struct options options; 356 357 pam_std_option(&options, other_options, argc, argv); 358 359 PAM_LOG("Options processed"); 360 361 PAM_RETURN(PAM_SUCCESS); 362} 363 364/* 365 * password management 366 * 367 * standard Unix and NIS password changing 368 */ 369PAM_EXTERN int 370pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 371{ 372 struct options options; 373 struct passwd *pwd; 374 const char *user, *pass, *new_pass; 375 char *encrypted, *usrdup; 376 int retval, res; 377 378 pam_std_option(&options, other_options, argc, argv); 379 380 PAM_LOG("Options processed"); 381 382 if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) 383 pwd = getpwnam(getlogin()); 384 else { 385 retval = pam_get_user(pamh, &user, NULL); 386 if (retval != PAM_SUCCESS) 387 PAM_RETURN(retval); 388 pwd = getpwnam(user); 389 } 390 391 PAM_LOG("Got user: %s", user); 392 393 if (flags & PAM_PRELIM_CHECK) { 394 395 PAM_LOG("PRELIM round; checking user password"); 396 397 if (pwd->pw_passwd[0] == '\0' 398 && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { 399 /* 400 * No password case. XXX Are we giving too much away 401 * by not prompting for a password? 402 * XXX check PAM_DISALLOW_NULL_AUTHTOK 403 */ 404 PAM_LOG("Got password"); 405 PAM_RETURN(PAM_SUCCESS); 406 } 407 else { 408 retval = pam_get_authtok(pamh, 409 PAM_OLDAUTHTOK, &pass, NULL); 410 if (retval != PAM_SUCCESS) 411 PAM_RETURN(retval); 412 PAM_LOG("Got password"); 413 } 414 encrypted = crypt(pass, pwd->pw_passwd); 415 if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') 416 encrypted = colon; 417 418 if (strcmp(encrypted, pwd->pw_passwd) != 0) { 419 pam_set_item(pamh, PAM_OLDAUTHTOK, NULL); 420 PAM_RETURN(PAM_AUTH_ERR); 421 } 422 423 PAM_RETURN(PAM_SUCCESS); 424 } 425 else if (flags & PAM_UPDATE_AUTHTOK) { 426 PAM_LOG("UPDATE round; checking user password"); 427 428 retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &pass, NULL); 429 if (retval != PAM_SUCCESS) 430 PAM_RETURN(retval); 431 432 PAM_LOG("Got old password"); 433 434 for (;;) { 435 retval = pam_get_authtok(pamh, 436 PAM_AUTHTOK, &new_pass, NULL); 437 if (retval != PAM_TRY_AGAIN) 438 break; 439 pam_error(pamh, "Mismatch; try again, EOF to quit."); 440 } 441 442 if (retval != PAM_SUCCESS) { 443 PAM_VERBOSE_ERROR("Unable to get new password"); 444 PAM_RETURN(PAM_PERM_DENIED); 445 } 446 447 PAM_LOG("Got new password: %s", new_pass); 448 449#ifdef YP 450 /* If NIS is set in the passwd database, use it */ 451 if ((usrdup = strdup(user)) == NULL) 452 PAM_RETURN(PAM_BUF_ERR); 453 res = use_yp(usrdup, 0, 0); 454 free(usrdup); 455 if (res == USER_YP_ONLY) { 456 if (!pam_test_option(&options, PAM_OPT_LOCAL_PASS, 457 NULL)) 458 retval = yp_passwd(user, new_pass); 459 else { 460 /* Reject 'local' flag if NIS is on and the user 461 * is not local 462 */ 463 retval = PAM_PERM_DENIED; 464 PAM_LOG("Unknown local user: %s", user); 465 } 466 } 467 else if (res == USER_LOCAL_ONLY) { 468 if (!pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) 469 retval = local_passwd(user, new_pass); 470 else { 471 /* Reject 'nis' flag if user is only local */ 472 retval = PAM_PERM_DENIED; 473 PAM_LOG("Unknown NIS user: %s", user); 474 } 475 } 476 else if (res == USER_YP_AND_LOCAL) { 477 if (pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) 478 retval = yp_passwd(user, new_pass); 479 else 480 retval = local_passwd(user, new_pass); 481 } 482 else 483 retval = PAM_SERVICE_ERR; /* Bad juju */ 484#else 485 retval = local_passwd(user, new_pass); 486#endif 487 } 488 else { 489 /* Very bad juju */ 490 retval = PAM_ABORT; 491 PAM_LOG("Illegal 'flags'"); 492 } 493 494 PAM_RETURN(retval); 495} 496 497/* Mostly stolen from passwd(1)'s local_passwd.c - markm */ 498 499static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ 500 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 501 502static void 503to64(char *s, long v, int n) 504{ 505 while (--n >= 0) { 506 *s++ = itoa64[v&0x3f]; 507 v >>= 6; 508 } 509} 510 511static int 512local_passwd(const char *user, const char *pass) 513{ 514 login_cap_t * lc; 515 struct passwd *pwd; 516 int pfd, tfd; 517 char *crypt_type, salt[SALTSIZE + 1]; 518 519 pwd = getpwnam(user); 520 if (pwd == NULL) 521 return(PAM_SERVICE_ERR); /* Really bad things */ 522 523#ifdef YP 524 pwd = (struct passwd *)&local_password; 525#endif 526 pw_init(); 527 528 pwd->pw_change = 0; 529 lc = login_getclass(NULL); 530 crypt_type = login_getcapstr(lc, "passwd_format", 531 password_hash, password_hash); 532 if (login_setcryptfmt(lc, crypt_type, NULL) == NULL) 533 syslog(LOG_ERR, "cannot set password cipher"); 534 login_close(lc); 535 makesalt(salt); 536 pwd->pw_passwd = crypt(pass, salt); 537 538 pfd = pw_lock(); 539 tfd = pw_tmp(); 540 pw_copy(pfd, tfd, pwd, NULL); 541 542 if (!pw_mkdb(user)) 543 pw_error((char *)NULL, 0, 1); 544 545 return (PAM_SUCCESS); 546} 547 548#ifdef YP 549/* Stolen from src/usr.bin/passwd/yp_passwd.c, carrying copyrights of: 550 * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca> 551 * Copyright (c) 1994 Olaf Kirch <okir@monad.swb.de> 552 * Copyright (c) 1995 Bill Paul <wpaul@ctr.columbia.edu> 553 */ 554int 555yp_passwd(const char *user __unused, const char *pass) 556{ 557 struct yppasswd yppwd; 558 struct master_yppasswd master_yppwd; 559 struct passwd *pwd; 560 struct rpc_err err; 561 CLIENT *clnt; 562 login_cap_t *lc; 563 int *status; 564 uid_t uid; 565 char *master, sockname[] = YP_SOCKNAME, salt[SALTSIZE + 1]; 566 567 _use_yp = 1; 568 569 uid = getuid(); 570 571 master = get_yp_master(1); 572 if (master == NULL) 573 return (PAM_SERVICE_ERR); /* Major disaster */ 574 575 /* 576 * It is presumed that by the time we get here, use_yp() 577 * has been called and that we have verified that the user 578 * actually exists. This being the case, the yp_password 579 * stucture has already been filled in for us. 580 */ 581 582 /* Use the correct password */ 583 pwd = (struct passwd *)&yp_password; 584 585 pwd->pw_change = 0; 586 587 /* Initialize password information */ 588 if (suser_override) { 589 master_yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd); 590 master_yppwd.newpw.pw_name = strdup(pwd->pw_name); 591 master_yppwd.newpw.pw_uid = pwd->pw_uid; 592 master_yppwd.newpw.pw_gid = pwd->pw_gid; 593 master_yppwd.newpw.pw_expire = pwd->pw_expire; 594 master_yppwd.newpw.pw_change = pwd->pw_change; 595 master_yppwd.newpw.pw_fields = pwd->pw_fields; 596 master_yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos); 597 master_yppwd.newpw.pw_dir = strdup(pwd->pw_dir); 598 master_yppwd.newpw.pw_shell = strdup(pwd->pw_shell); 599 master_yppwd.newpw.pw_class = pwd->pw_class != NULL ? 600 strdup(pwd->pw_class) : strdup(""); 601 master_yppwd.oldpass = strdup(""); 602 master_yppwd.domain = yp_domain; 603 } else { 604 yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd); 605 yppwd.newpw.pw_name = strdup(pwd->pw_name); 606 yppwd.newpw.pw_uid = pwd->pw_uid; 607 yppwd.newpw.pw_gid = pwd->pw_gid; 608 yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos); 609 yppwd.newpw.pw_dir = strdup(pwd->pw_dir); 610 yppwd.newpw.pw_shell = strdup(pwd->pw_shell); 611 yppwd.oldpass = strdup(""); 612 } 613 614 if (login_setcryptfmt(lc, "md5", NULL) == NULL) 615 syslog(LOG_ERR, "cannot set password cipher"); 616 login_close(lc); 617 618 makesalt(salt); 619 if (suser_override) 620 master_yppwd.newpw.pw_passwd = crypt(pass, salt); 621 else 622 yppwd.newpw.pw_passwd = crypt(pass, salt); 623 624 if (suser_override) { 625 if ((clnt = clnt_create(sockname, MASTER_YPPASSWDPROG, 626 MASTER_YPPASSWDVERS, "unix")) == NULL) { 627 syslog(LOG_ERR, 628 "Cannot contact rpc.yppasswdd on host %s: %s", 629 master, clnt_spcreateerror("")); 630 return (PAM_SERVICE_ERR); 631 } 632 } 633 else { 634 if ((clnt = clnt_create(master, YPPASSWDPROG, 635 YPPASSWDVERS, "udp")) == NULL) { 636 syslog(LOG_ERR, 637 "Cannot contact rpc.yppasswdd on host %s: %s", 638 master, clnt_spcreateerror("")); 639 return (PAM_SERVICE_ERR); 640 } 641 } 642 /* 643 * The yppasswd.x file said `unix authentication required', 644 * so I added it. This is the only reason it is in here. 645 * My yppasswdd doesn't use it, but maybe some others out there 646 * do. --okir 647 */ 648 clnt->cl_auth = authunix_create_default(); 649 650 if (suser_override) 651 status = yppasswdproc_update_master_1(&master_yppwd, clnt); 652 else 653 status = yppasswdproc_update_1(&yppwd, clnt); 654 655 clnt_geterr(clnt, &err); 656 657 auth_destroy(clnt->cl_auth); 658 clnt_destroy(clnt); 659 660 if (err.re_status != RPC_SUCCESS || status == NULL || *status) 661 return (PAM_SERVICE_ERR); 662 663 if (err.re_status || status == NULL || *status) 664 return (PAM_SERVICE_ERR); 665 return (PAM_SUCCESS); 666} 667#endif /* YP */ 668 669/* Salt suitable for traditional DES and MD5 */ 670void 671makesalt(char salt[SALTSIZE]) 672{ 673 int i; 674 675 /* These are not really random numbers, they are just 676 * numbers that change to thwart construction of a 677 * dictionary. This is exposed to the public. 678 */ 679 for (i = 0; i < SALTSIZE; i += 4) 680 to64(&salt[i], arc4random(), 4); 681 salt[SALTSIZE] = '\0'; 682} 683 684PAM_MODULE_ENTRY("pam_unix"); 685