1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #include <syslog.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <stdarg.h> 21 #include <string.h> 22 #include <unistd.h> 23 #include <sys/stat.h> 24 #include <fcntl.h> 25 26 #include <security/pam_modules.h> 27 #include <security/pam_ext.h> 28 #include <security/pam_modutil.h> 29 30 #include <openssl/evp.h> 31 #include <openssl/hmac.h> 32 #include <openssl/rand.h> 33 34 /* 35 * This module is intended to save password of special group user 36 * 37 */ 38 39 #define MAX_SPEC_GRP_PASS_LENGTH 20 40 #define MAX_SPEC_GRP_USER_LENGTH 16 41 #define MAX_KEY_SIZE 8 42 #define DEFAULT_SPEC_PASS_FILE "/etc/ipmi_pass" 43 #define META_PASSWD_SIG "=OPENBMC=" 44 45 /* 46 * Meta data struct for storing the encrypted password file 47 * Note: Followed by this structure, the real data of hash, iv, encrypted data 48 * with pad and mac are stored. 49 * Decrypted data will hold user name & password for every new line with format 50 * like <user name>:<password>\n 51 */ 52 typedef struct metapassstruct { 53 char signature[10]; 54 unsigned char reseved[2]; 55 size_t hashsize; 56 size_t ivsize; 57 size_t datasize; 58 size_t padsize; 59 size_t macsize; 60 } metapassstruct; 61 62 /** 63 * @brief to acquire lock for atomic operation 64 * Internally uses lckpwdf to acquire the lock. Tries to acquire the lock 65 * using lckpwdf() in interval of 1ms, with maximum of 100 attempts. 66 * 67 * @return PAM_SUCCESS for success / PAM_AUTHTOK_LOCK_BUSY for failure 68 */ 69 int lock_pwdf(void) 70 { 71 int i; 72 int retval; 73 74 i = 0; 75 while ((retval = lckpwdf()) != 0 && i < 100) { 76 usleep(1000); 77 i++; 78 } 79 if (retval != 0) { 80 return PAM_AUTHTOK_LOCK_BUSY; 81 } 82 return PAM_SUCCESS; 83 } 84 85 /** 86 * @brief unlock the acquired lock 87 * Internally uses ulckpwdf to release the lock 88 */ 89 void unlock_pwdf(void) 90 { 91 ulckpwdf(); 92 } 93 94 /** 95 * @brief to get argument value of option 96 * Function to get the value of argument options. 97 * 98 * @param[in] pamh - pam handle 99 * @param[in] option - argument option to which value has to returned 100 * @param[in] argc - argument count 101 * @param[in] argv - array of arguments 102 */ 103 static const char *get_option(const pam_handle_t *pamh, const char *option, 104 int argc, const char **argv) 105 { 106 int i; 107 size_t len; 108 109 len = strlen(option); 110 111 for (i = 0; i < argc; ++i) { 112 if (strncmp(option, argv[i], len) == 0) { 113 if (argv[i][len] == '=') { 114 return &argv[i][len + 1]; 115 } 116 } 117 } 118 return NULL; 119 } 120 121 /** 122 * @brief encrypt or decrypt function 123 * Function which will do the encryption or decryption of the data. 124 * 125 * @param[in] pamh - pam handle. 126 * @param[in] isencrypt - encrypt or decrypt option. 127 * @param[in] cipher - EVP_CIPHER to be used 128 * @param[in] key - key which has to be used in EVP_CIPHER api's. 129 * @param[in] keylen - Length of the key. 130 * @param[in] iv - Initialization vector data, used along with key 131 * @param[in] ivlen - Length of IV. 132 * @param[in] inbytes - buffer which has to be encrypted or decrypted. 133 * @param[in] inbyteslen - length of input buffer. 134 * @param[in] outbytes - buffer to store decrypted or encrypted data. 135 * @param[in] outbyteslen - length of output buffer 136 * @param[in/out] mac - checksum to cross verify. Will be verified for decrypt 137 * and returns for encrypt. 138 * @param[in/out] maclen - length of checksum 139 * @return - 0 for success -1 for failures. 140 */ 141 int encrypt_decrypt_data(const pam_handle_t *pamh, int isencrypt, 142 const EVP_CIPHER *cipher, const char *key, 143 size_t keylen, const char *iv, size_t ivlen, 144 const char *inbytes, size_t inbyteslen, char *outbytes, 145 size_t *outbyteslen, char *mac, size_t *maclen) 146 { 147 EVP_CIPHER_CTX *ctx; 148 const EVP_MD *digest; 149 size_t outEVPlen = 0; 150 int retval = 0; 151 size_t outlen = 0; 152 153 if (cipher == NULL || key == NULL || iv == NULL || inbytes == NULL 154 || outbytes == NULL || mac == NULL || inbyteslen == 0 155 || EVP_CIPHER_key_length(cipher) > keylen 156 || EVP_CIPHER_iv_length(cipher) > ivlen) { 157 pam_syslog(pamh, LOG_DEBUG, "Invalid inputs"); 158 return -1; 159 } 160 161 digest = EVP_sha256(); 162 if (!isencrypt) { 163 char calmac[EVP_MAX_MD_SIZE]; 164 size_t calmaclen = 0; 165 // calculate MAC for the encrypted message. 166 if (NULL 167 == HMAC(digest, key, keylen, inbytes, inbyteslen, calmac, 168 &calmaclen)) { 169 pam_syslog(pamh, LOG_DEBUG, 170 "Failed to verify authentication %d", 171 retval); 172 return -1; 173 } 174 if (!((calmaclen == *maclen) 175 && (memcmp(calmac, mac, calmaclen) == 0))) { 176 pam_syslog(pamh, LOG_DEBUG, 177 "Authenticated message doesn't match %d, %d", 178 calmaclen, *maclen); 179 return -1; 180 } 181 } 182 183 ctx = EVP_CIPHER_CTX_new(); 184 EVP_CIPHER_CTX_set_padding(ctx, 1); 185 186 // Set key & IV 187 retval = EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, isencrypt); 188 if (!retval) { 189 pam_syslog(pamh, LOG_DEBUG, "EVP_CipherInit_ex failed with %d", 190 retval); 191 EVP_CIPHER_CTX_free(ctx); 192 return -1; 193 } 194 if ((retval = EVP_CipherUpdate(ctx, outbytes + outlen, &outEVPlen, 195 inbytes, inbyteslen))) { 196 outlen += outEVPlen; 197 if ((retval = EVP_CipherFinal(ctx, outbytes + outlen, 198 &outEVPlen))) { 199 outlen += outEVPlen; 200 *outbyteslen = outlen; 201 } else { 202 pam_syslog(pamh, LOG_DEBUG, 203 "EVP_CipherFinal returns with %d", retval); 204 EVP_CIPHER_CTX_free(ctx); 205 return -1; 206 } 207 } else { 208 pam_syslog(pamh, LOG_DEBUG, "EVP_CipherUpdate returns with %d", 209 retval); 210 EVP_CIPHER_CTX_free(ctx); 211 return -1; 212 } 213 EVP_CIPHER_CTX_free(ctx); 214 215 if (isencrypt) { 216 // Create MAC for the encrypted message. 217 if (NULL 218 == HMAC(digest, key, keylen, outbytes, *outbyteslen, mac, 219 maclen)) { 220 pam_syslog(pamh, LOG_DEBUG, 221 "Failed to create authentication %d", 222 retval); 223 return -1; 224 } 225 } 226 return 0; 227 } 228 229 230 /** 231 * @brief get temporary file handle 232 * Function to get the temporary file handle, created using mkstemp 233 * 234 * @param[in] pamh - pam handle. 235 * @param[in/out] tempfilename - tempfilename, which will be used in mkstemp. 236 * @return - FILE handle for success. NULL for failure 237 */ 238 FILE *get_temp_file_handle(const pam_handle_t *pamh, char *const tempfilename) 239 { 240 FILE *tempfile = NULL; 241 int fd; 242 int oldmask = umask(077); 243 fd = mkstemp(tempfilename); 244 if (fd == -1) { 245 pam_syslog(pamh, LOG_DEBUG, "Error in creating temp file"); 246 umask(oldmask); 247 return NULL; 248 } 249 pam_syslog(pamh, LOG_DEBUG, "Temporary file name is %s", tempfilename); 250 251 tempfile = fdopen(fd, "w"); 252 umask(oldmask); 253 return tempfile; 254 } 255 256 257 /** 258 * @brief updates special password file 259 * Function to update the special password file. Stores the password against 260 * username in encrypted form along with meta data 261 * 262 * @param[in] pamh - pam handle. 263 * @param[in] keyfilename - file name where key seed is stored. 264 * @param[in] filename - special password file name 265 * @param[in] forwho - name of the user 266 * @param[in] towhat - password that has to stored in encrypted form 267 * @return - PAM_SUCCESS for success or PAM_AUTHTOK_ERR for failure 268 */ 269 int update_pass_special_file(const pam_handle_t *pamh, const char *keyfilename, 270 const char *filename, const char *forwho, 271 const char *towhat) 272 { 273 struct stat st; 274 FILE *pwfile = NULL, *opwfile = NULL, *keyfile = NULL; 275 int err = 0, wroteentry = 0; 276 char tempfilename[1024]; 277 size_t forwholen = strlen(forwho); 278 size_t towhatlen = strlen(towhat); 279 char keybuff[MAX_KEY_SIZE] = {0}; 280 size_t keybuffsize = sizeof(keybuff); 281 282 const EVP_CIPHER *cipher = EVP_aes_128_cbc(); 283 const EVP_MD *digest = EVP_sha256(); 284 285 char *linebuff = NULL, *opwfilebuff = NULL, *opwptext = NULL; 286 size_t opwptextlen = 0, opwfilesize = 0; 287 metapassstruct *opwmp = NULL; 288 289 char *pwptext = NULL, *pwctext = NULL; 290 size_t pwctextlen = 0, pwptextlen = 0, maclen = 0; 291 size_t writtensize = 0, keylen = 0; 292 metapassstruct pwmp = {META_PASSWD_SIG, {0, 0}, .0, 0, 0, 0, 0}; 293 char mac[EVP_MAX_MD_SIZE] = {0}; 294 unsigned char key[EVP_MAX_KEY_LENGTH]; 295 char iv[EVP_CIPHER_iv_length(cipher)]; 296 char hash[EVP_MD_block_size(digest)]; 297 298 // Following steps are performed in this function. 299 // Step 1: Create a temporary file - always update temporary file, and 300 // then swap it with original one, only if everything succeded at the 301 // end. Step 2: If file already exists, read the old file and decrypt it 302 // in buffer Step 3: Copy user/password pair from old buffer to new 303 // buffer, and update, if the user already exists with the new password 304 // Step 4: Encrypt the new buffer and write it to the temp file created 305 // at Step 1. 306 // Step 5. rename the temporary file name as special password file. 307 308 // verify the tempfilename buffer is enough to hold 309 // filename_XXXXXX (+1 for null). 310 if (strlen(filename) 311 > (sizeof(tempfilename) - strlen("__XXXXXX") - 1)) { 312 pam_syslog(pamh, LOG_DEBUG, "Not enough buffer, bailing out"); 313 return PAM_AUTHTOK_ERR; 314 } 315 // Fetch the key from key file name. 316 keyfile = fopen(keyfilename, "r"); 317 if (keyfile == NULL) { 318 pam_syslog(pamh, LOG_DEBUG, "Unable to open key file %s", 319 keyfilename); 320 return PAM_AUTHTOK_ERR; 321 } 322 if (fread(keybuff, 1, keybuffsize, keyfile) != keybuffsize) { 323 pam_syslog(pamh, LOG_DEBUG, "Key file read failed"); 324 fclose(keyfile); 325 return PAM_AUTHTOK_ERR; 326 } 327 fclose(keyfile); 328 329 // Step 1: Try to create a temporary file, in which all the update will 330 // happen then it will be renamed to the original file. This is done to 331 // have atomic operation. 332 snprintf(tempfilename, sizeof(tempfilename), "%s__XXXXXX", filename); 333 pwfile = get_temp_file_handle(pamh, tempfilename); 334 if (pwfile == NULL) { 335 err = 1; 336 goto done; 337 } 338 339 // Update temporary file stat by reading the special password file 340 opwfile = fopen(filename, "r"); 341 if (opwfile != NULL) { 342 if (fstat(fileno(opwfile), &st) == -1) { 343 fclose(opwfile); 344 fclose(pwfile); 345 err = 1; 346 goto done; 347 } 348 } else { // Create with this settings if file is not present. 349 memset(&st, 0, sizeof(st)); 350 } 351 // Override the file permission with S_IWUSR | S_IRUSR 352 st.st_mode = S_IWUSR | S_IRUSR; 353 if ((fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) 354 || (fchmod(fileno(pwfile), st.st_mode) == -1)) { 355 if (opwfile != NULL) { 356 fclose(opwfile); 357 } 358 fclose(pwfile); 359 err = 1; 360 goto done; 361 } 362 opwfilesize = st.st_size; 363 364 // Step 2: Read existing special password file and decrypt the data. 365 if (opwfilesize) { 366 opwfilebuff = malloc(opwfilesize); 367 if (opwfilebuff == NULL) { 368 fclose(opwfile); 369 fclose(pwfile); 370 err = 1; 371 goto done; 372 } 373 374 if (fread(opwfilebuff, 1, opwfilesize, opwfile)) { 375 opwmp = (metapassstruct *)opwfilebuff; 376 opwptext = malloc(opwmp->datasize + opwmp->padsize); 377 if (opwptext == NULL) { 378 free(opwfilebuff); 379 fclose(opwfile); 380 fclose(pwfile); 381 err = 1; 382 goto done; 383 } 384 // User & password pairs are mapped as <user 385 // name>:<password>\n. Add +3 for special chars ':', 386 // '\n' and '\0'. 387 pwptextlen = opwmp->datasize + forwholen + towhatlen + 3 388 + EVP_CIPHER_block_size(cipher); 389 pwptext = malloc(pwptextlen); 390 if (pwptext == NULL) { 391 free(opwptext); 392 free(opwfilebuff); 393 fclose(opwfile); 394 fclose(pwfile); 395 err = 1; 396 goto done; 397 } 398 399 // First get the hashed key to decrypt 400 HMAC(digest, keybuff, keybuffsize, 401 opwfilebuff + sizeof(*opwmp), opwmp->hashsize, key, 402 &keylen); 403 404 // Skip decryption if there is no data 405 if (opwmp->datasize != 0) { 406 // Do the decryption 407 if (encrypt_decrypt_data( 408 pamh, 0, cipher, key, keylen, 409 opwfilebuff + sizeof(*opwmp) 410 + opwmp->hashsize, 411 opwmp->ivsize, 412 opwfilebuff + sizeof(*opwmp) 413 + opwmp->hashsize 414 + opwmp->ivsize, 415 opwmp->datasize + opwmp->padsize, 416 opwptext, &opwptextlen, 417 opwfilebuff + sizeof(*opwmp) 418 + opwmp->hashsize 419 + opwmp->ivsize 420 + opwmp->datasize 421 + opwmp->padsize, 422 &opwmp->macsize) 423 != 0) { 424 pam_syslog(pamh, LOG_DEBUG, 425 "Decryption failed"); 426 free(pwptext); 427 free(opwptext); 428 free(opwfilebuff); 429 fclose(opwfile); 430 fclose(pwfile); 431 err = 1; 432 goto done; 433 } 434 } 435 436 // NULL terminate it, before using it in strtok(). 437 opwptext[opwmp->datasize] = '\0'; 438 439 linebuff = strtok(opwptext, "\n"); 440 // Step 3: Copy the existing user/password pair 441 // to the new buffer, and update the password if user 442 // already exists. 443 while (linebuff != NULL) { 444 if ((!strncmp(linebuff, forwho, forwholen)) 445 && (linebuff[forwholen] == ':')) { 446 writtensize += snprintf( 447 pwptext + writtensize, 448 pwptextlen - writtensize, 449 "%s:%s\n", forwho, towhat); 450 wroteentry = 1; 451 } else { 452 writtensize += snprintf( 453 pwptext + writtensize, 454 pwptextlen - writtensize, 455 "%s\n", linebuff); 456 } 457 linebuff = strtok(NULL, "\n"); 458 } 459 } 460 // Clear the old password related buffers here, as we are done 461 // with it. 462 free(opwfilebuff); 463 free(opwptext); 464 } else { 465 pwptextlen = forwholen + towhatlen + 3 466 + EVP_CIPHER_block_size(cipher); 467 pwptext = malloc(pwptextlen); 468 if (pwptext == NULL) { 469 if (opwfile != NULL) { 470 fclose(opwfile); 471 } 472 fclose(pwfile); 473 err = 1; 474 goto done; 475 } 476 } 477 478 if (opwfile != NULL) { 479 fclose(opwfile); 480 } 481 482 if (!wroteentry) { 483 // Write the new user:password pair at the end. 484 writtensize += snprintf(pwptext + writtensize, 485 pwptextlen - writtensize, "%s:%s\n", 486 forwho, towhat); 487 } 488 pwptextlen = writtensize; 489 490 // Step 4: Encrypt the data and write to the temporary file 491 if (RAND_bytes(hash, EVP_MD_block_size(digest)) != 1) { 492 pam_syslog(pamh, LOG_DEBUG, 493 "Hash genertion failed, bailing out"); 494 free(pwptext); 495 fclose(pwfile); 496 err = 1; 497 goto done; 498 } 499 500 // Generate hash key, which will be used for encryption. 501 HMAC(digest, keybuff, keybuffsize, hash, EVP_MD_block_size(digest), key, 502 &keylen); 503 // Generate IV values 504 if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) { 505 pam_syslog(pamh, LOG_DEBUG, 506 "IV generation failed, bailing out"); 507 free(pwptext); 508 fclose(pwfile); 509 err = 1; 510 goto done; 511 } 512 513 // Buffer to store encrypted message. 514 pwctext = malloc(pwptextlen + EVP_CIPHER_block_size(cipher)); 515 if (pwctext == NULL) { 516 pam_syslog(pamh, LOG_DEBUG, "Ctext buffer failed, bailing out"); 517 free(pwptext); 518 fclose(pwfile); 519 err = 1; 520 goto done; 521 } 522 523 // Do the encryption 524 if (encrypt_decrypt_data(pamh, 1, cipher, key, keylen, iv, 525 EVP_CIPHER_iv_length(cipher), pwptext, 526 pwptextlen, pwctext, &pwctextlen, mac, &maclen) 527 != 0) { 528 pam_syslog(pamh, LOG_DEBUG, "Encryption failed"); 529 free(pwctext); 530 free(pwptext); 531 fclose(pwfile); 532 err = 1; 533 goto done; 534 } 535 536 // Update the meta password structure. 537 pwmp.hashsize = EVP_MD_block_size(digest); 538 pwmp.ivsize = EVP_CIPHER_iv_length(cipher); 539 pwmp.datasize = writtensize; 540 pwmp.padsize = pwctextlen - writtensize; 541 pwmp.macsize = maclen; 542 543 // Write the meta password structure, followed by hash, iv, encrypted 544 // data & mac. 545 if (fwrite(&pwmp, 1, sizeof(pwmp), pwfile) != sizeof(pwmp)) { 546 pam_syslog(pamh, LOG_DEBUG, "Error in writing meta data"); 547 err = 1; 548 } 549 if (fwrite(hash, 1, pwmp.hashsize, pwfile) != pwmp.hashsize) { 550 pam_syslog(pamh, LOG_DEBUG, "Error in writing hash data"); 551 err = 1; 552 } 553 if (fwrite(iv, 1, pwmp.ivsize, pwfile) != pwmp.ivsize) { 554 pam_syslog(pamh, LOG_DEBUG, "Error in writing IV data"); 555 err = 1; 556 } 557 if (fwrite(pwctext, 1, pwctextlen, pwfile) != pwctextlen) { 558 pam_syslog(pamh, LOG_DEBUG, "Error in encrypted data"); 559 err = 1; 560 } 561 if (fwrite(mac, 1, maclen, pwfile) != maclen) { 562 pam_syslog(pamh, LOG_DEBUG, "Error in writing MAC"); 563 err = 1; 564 } 565 566 free(pwctext); 567 free(pwptext); 568 569 if (fflush(pwfile) || fsync(fileno(pwfile))) { 570 pam_syslog( 571 pamh, LOG_DEBUG, 572 "fflush or fsync error writing entries to special file: %s", 573 tempfilename); 574 err = 1; 575 } 576 577 if (fclose(pwfile)) { 578 pam_syslog(pamh, LOG_DEBUG, 579 "fclose error writing entries to special file: %s", 580 tempfilename); 581 err = 1; 582 } 583 584 done: 585 if (!err) { 586 // Step 5: Rename the temporary file as special password file. 587 if (!rename(tempfilename, filename)) { 588 pam_syslog(pamh, LOG_DEBUG, 589 "password changed for %s in special file", 590 forwho); 591 } else { 592 err = 1; 593 } 594 } 595 596 // Clear out the key buff. 597 memset(keybuff, 0, keybuffsize); 598 599 if (!err) { 600 return PAM_SUCCESS; 601 } else { 602 unlink(tempfilename); 603 return PAM_AUTHTOK_ERR; 604 } 605 } 606 607 608 /* Password Management API's */ 609 610 /** 611 * @brief pam_sm_chauthtok API 612 * Function which will be called for pam_chauthtok() calls. 613 * 614 * @param[in] pamh - pam handle 615 * @param[in] flags - pam calls related flags 616 * @param[in] argc - argument counts / options 617 * @param[in] argv - array of arguments / options 618 * @return - PAM_SUCCESS for success, others for failure 619 */ 620 int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 621 { 622 int retval = -1; 623 const void *item = NULL; 624 const char *user = NULL; 625 const char *pass_new = NULL, *pass_old = NULL; 626 const char *spec_grp_name = 627 get_option(pamh, "spec_grp_name", argc, argv); 628 const char *spec_pass_file = 629 get_option(pamh, "spec_pass_file", argc, argv); 630 const char *key_file = get_option(pamh, "key_file", argc, argv); 631 632 633 if (spec_grp_name == NULL || key_file == NULL) { 634 return PAM_IGNORE; 635 } 636 if (flags & PAM_PRELIM_CHECK) { 637 // send success to verify other stacked modules prelim check. 638 return PAM_SUCCESS; 639 } 640 641 retval = pam_get_user(pamh, &user, NULL); 642 if (retval != PAM_SUCCESS) { 643 return retval; 644 } 645 646 // get already read password by the stacked pam module 647 // Note: If there are no previous stacked pam module which read 648 // the new password, then return with AUTHTOK_ERR 649 650 retval = pam_get_item(pamh, PAM_AUTHTOK, &item); 651 if (retval != PAM_SUCCESS || item == NULL) { 652 return PAM_AUTHTOK_ERR; 653 } 654 pass_new = item; 655 656 struct group *grp; 657 int spec_grp_usr = 0; 658 // Verify whether the user belongs to special group. 659 grp = pam_modutil_getgrnam(pamh, spec_grp_name); 660 if (grp != NULL) { 661 while (*(grp->gr_mem) != NULL) { 662 if (strcmp(user, *grp->gr_mem) == 0) { 663 spec_grp_usr = 1; 664 break; 665 } 666 (grp->gr_mem)++; 667 } 668 } 669 670 pam_syslog(pamh, LOG_DEBUG, "User belongs to special grp: %x", 671 spec_grp_usr); 672 673 if (spec_grp_usr) { 674 // verify the new password is acceptable. 675 if (strlen(pass_new) > MAX_SPEC_GRP_PASS_LENGTH 676 || strlen(user) > MAX_SPEC_GRP_USER_LENGTH) { 677 pam_syslog( 678 pamh, LOG_ERR, 679 "Password length (%x) / User name length (%x) not acceptable", 680 strlen(pass_new), strlen(user)); 681 pass_new = NULL; 682 return PAM_AUTHTOK_ERR; 683 } 684 if (spec_pass_file == NULL) { 685 spec_pass_file = DEFAULT_SPEC_PASS_FILE; 686 pam_syslog( 687 pamh, LOG_ERR, 688 "Using default special password file name :%s", 689 spec_pass_file); 690 } 691 if (retval = lock_pwdf()) { 692 pam_syslog(pamh, LOG_ERR, 693 "Failed to lock the passwd file"); 694 return retval; 695 } 696 retval = update_pass_special_file( 697 pamh, key_file, spec_pass_file, user, pass_new); 698 unlock_pwdf(); 699 return retval; 700 } 701 702 return PAM_SUCCESS; 703 } 704 705 /* end of module definition */ 706