blob: f53ea865ab6066e783ef95271b1b0e6d42f1098c [file] [log] [blame]
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +05301/*
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="
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +053044
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 */
52typedef 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 */
69int 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 */
89void 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 */
103static 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 */
141int 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 */
Patrick Venture9565abd2018-11-14 09:11:59 -0800238FILE *get_temp_file_handle(const pam_handle_t *pamh, char *const tempfilename)
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530239{
240 FILE *tempfile = NULL;
Patrick Ventured0e324a2018-11-14 10:18:50 -0800241 int fd;
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530242 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 */
269int 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
Patrick Venture9565abd2018-11-14 09:11:59 -0800300 // 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
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530304 // 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
Patrick Venture9565abd2018-11-14 09:11:59 -0800329 // 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.
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530332 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 st.st_mode = 0x8000 | S_IRUSR;
351 }
Patrick Venture9565abd2018-11-14 09:11:59 -0800352 if ((fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1)
353 || (fchmod(fileno(pwfile), st.st_mode) == -1)) {
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530354 if (opwfile != NULL) {
355 fclose(opwfile);
356 }
357 fclose(pwfile);
358 err = 1;
359 goto done;
360 }
361 opwfilesize = st.st_size;
362
363 // Step 2: Read existing special password file and decrypt the data.
364 if (opwfilesize) {
365 opwfilebuff = malloc(opwfilesize);
366 if (opwfilebuff == NULL) {
367 fclose(opwfile);
368 fclose(pwfile);
369 err = 1;
370 goto done;
371 }
372
373 if (fread(opwfilebuff, 1, opwfilesize, opwfile)) {
374 opwmp = (metapassstruct *)opwfilebuff;
375 opwptext = malloc(opwmp->datasize + opwmp->padsize);
376 if (opwptext == NULL) {
377 free(opwfilebuff);
378 fclose(opwfile);
379 fclose(pwfile);
380 err = 1;
381 goto done;
382 }
Patrick Venture9565abd2018-11-14 09:11:59 -0800383 // User & password pairs are mapped as <user
384 // name>:<password>\n. Add +3 for special chars ':',
385 // '\n' and '\0'.
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530386 pwptextlen = opwmp->datasize + forwholen + towhatlen + 3
387 + EVP_CIPHER_block_size(cipher);
388 pwptext = malloc(pwptextlen);
389 if (pwptext == NULL) {
390 free(opwptext);
391 free(opwfilebuff);
392 fclose(opwfile);
393 fclose(pwfile);
394 err = 1;
395 goto done;
396 }
397
398 // First get the hashed key to decrypt
399 HMAC(digest, keybuff, keybuffsize,
400 opwfilebuff + sizeof(*opwmp), opwmp->hashsize, key,
401 &keylen);
402
403 // Skip decryption if there is no data
404 if (opwmp->datasize != 0) {
405 // Do the decryption
406 if (encrypt_decrypt_data(
407 pamh, 0, cipher, key, keylen,
408 opwfilebuff + sizeof(*opwmp)
Patrick Venture9565abd2018-11-14 09:11:59 -0800409 + opwmp->hashsize,
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530410 opwmp->ivsize,
411 opwfilebuff + sizeof(*opwmp)
Patrick Venture9565abd2018-11-14 09:11:59 -0800412 + opwmp->hashsize
413 + opwmp->ivsize,
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530414 opwmp->datasize + opwmp->padsize,
415 opwptext, &opwptextlen,
416 opwfilebuff + sizeof(*opwmp)
Patrick Venture9565abd2018-11-14 09:11:59 -0800417 + opwmp->hashsize
418 + opwmp->ivsize
419 + opwmp->datasize
420 + opwmp->padsize,
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530421 &opwmp->macsize)
422 != 0) {
423 pam_syslog(pamh, LOG_DEBUG,
424 "Decryption failed");
425 free(pwptext);
426 free(opwptext);
427 free(opwfilebuff);
428 fclose(opwfile);
429 fclose(pwfile);
430 err = 1;
431 goto done;
432 }
433 }
434
435 // NULL terminate it, before using it in strtok().
436 opwptext[opwmp->datasize] = '\0';
437
438 linebuff = strtok(opwptext, "\n");
439 // Step 3: Copy the existing user/password pair
440 // to the new buffer, and update the password if user
441 // already exists.
442 while (linebuff != NULL) {
443 if ((!strncmp(linebuff, forwho, forwholen))
444 && (linebuff[forwholen] == ':')) {
445 writtensize += snprintf(
Patrick Venture9565abd2018-11-14 09:11:59 -0800446 pwptext + writtensize,
447 pwptextlen - writtensize,
448 "%s:%s\n", forwho, towhat);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530449 wroteentry = 1;
450 } else {
451 writtensize += snprintf(
Patrick Venture9565abd2018-11-14 09:11:59 -0800452 pwptext + writtensize,
453 pwptextlen - writtensize,
454 "%s\n", linebuff);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530455 }
456 linebuff = strtok(NULL, "\n");
457 }
458 }
459 // Clear the old password related buffers here, as we are done
460 // with it.
461 free(opwfilebuff);
462 free(opwptext);
463 } else {
464 pwptextlen = forwholen + towhatlen + 3
465 + EVP_CIPHER_block_size(cipher);
466 pwptext = malloc(pwptextlen);
467 if (pwptext == NULL) {
468 if (opwfile != NULL) {
469 fclose(opwfile);
470 }
471 fclose(pwfile);
472 err = 1;
473 goto done;
474 }
475 }
476
477 if (opwfile != NULL) {
478 fclose(opwfile);
479 }
480
Richard Marian Thomaiyar65edb932019-01-28 20:39:10 +0530481 if (!wroteentry) {
482 // Write the new user:password pair at the end.
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530483 writtensize += snprintf(pwptext + writtensize,
484 pwptextlen - writtensize, "%s:%s\n",
485 forwho, towhat);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530486 }
Richard Marian Thomaiyar65edb932019-01-28 20:39:10 +0530487 pwptextlen = writtensize;
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530488
489 // Step 4: Encrypt the data and write to the temporary file
490 if (RAND_bytes(hash, EVP_MD_block_size(digest)) != 1) {
491 pam_syslog(pamh, LOG_DEBUG,
492 "Hash genertion failed, bailing out");
493 free(pwptext);
494 fclose(pwfile);
495 err = 1;
496 goto done;
497 }
498
499 // Generate hash key, which will be used for encryption.
500 HMAC(digest, keybuff, keybuffsize, hash, EVP_MD_block_size(digest), key,
501 &keylen);
502 // Generate IV values
503 if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) {
504 pam_syslog(pamh, LOG_DEBUG,
505 "IV generation failed, bailing out");
506 free(pwptext);
507 fclose(pwfile);
508 err = 1;
509 goto done;
510 }
511
512 // Buffer to store encrypted message.
513 pwctext = malloc(pwptextlen + EVP_CIPHER_block_size(cipher));
514 if (pwctext == NULL) {
515 pam_syslog(pamh, LOG_DEBUG, "Ctext buffer failed, bailing out");
516 free(pwptext);
517 fclose(pwfile);
518 err = 1;
519 goto done;
520 }
521
522 // Do the encryption
523 if (encrypt_decrypt_data(pamh, 1, cipher, key, keylen, iv,
524 EVP_CIPHER_iv_length(cipher), pwptext,
525 pwptextlen, pwctext, &pwctextlen, mac, &maclen)
526 != 0) {
527 pam_syslog(pamh, LOG_DEBUG, "Encryption failed");
528 free(pwctext);
529 free(pwptext);
530 fclose(pwfile);
531 err = 1;
532 goto done;
533 }
534
535 // Update the meta password structure.
536 pwmp.hashsize = EVP_MD_block_size(digest);
537 pwmp.ivsize = EVP_CIPHER_iv_length(cipher);
538 pwmp.datasize = writtensize;
539 pwmp.padsize = pwctextlen - writtensize;
540 pwmp.macsize = maclen;
541
542 // Write the meta password structure, followed by hash, iv, encrypted
543 // data & mac.
544 if (fwrite(&pwmp, 1, sizeof(pwmp), pwfile) != sizeof(pwmp)) {
545 pam_syslog(pamh, LOG_DEBUG, "Error in writing meta data");
546 err = 1;
547 }
548 if (fwrite(hash, 1, pwmp.hashsize, pwfile) != pwmp.hashsize) {
549 pam_syslog(pamh, LOG_DEBUG, "Error in writing hash data");
550 err = 1;
551 }
552 if (fwrite(iv, 1, pwmp.ivsize, pwfile) != pwmp.ivsize) {
553 pam_syslog(pamh, LOG_DEBUG, "Error in writing IV data");
554 err = 1;
555 }
556 if (fwrite(pwctext, 1, pwctextlen, pwfile) != pwctextlen) {
557 pam_syslog(pamh, LOG_DEBUG, "Error in encrypted data");
558 err = 1;
559 }
560 if (fwrite(mac, 1, maclen, pwfile) != maclen) {
561 pam_syslog(pamh, LOG_DEBUG, "Error in writing MAC");
562 err = 1;
563 }
564
565 free(pwctext);
566 free(pwptext);
567
568 if (fflush(pwfile) || fsync(fileno(pwfile))) {
569 pam_syslog(
570 pamh, LOG_DEBUG,
571 "fflush or fsync error writing entries to special file: %s",
572 tempfilename);
573 err = 1;
574 }
575
576 if (fclose(pwfile)) {
577 pam_syslog(pamh, LOG_DEBUG,
578 "fclose error writing entries to special file: %s",
579 tempfilename);
580 err = 1;
581 }
582
583done:
584 if (!err) {
585 // Step 5: Rename the temporary file as special password file.
586 if (!rename(tempfilename, filename)) {
587 pam_syslog(pamh, LOG_DEBUG,
588 "password changed for %s in special file",
589 forwho);
590 } else {
591 err = 1;
592 }
593 }
594
595 // Clear out the key buff.
596 memset(keybuff, 0, keybuffsize);
597
598 if (!err) {
599 return PAM_SUCCESS;
600 } else {
601 unlink(tempfilename);
602 return PAM_AUTHTOK_ERR;
603 }
604}
605
606
607/* Password Management API's */
608
609/**
610 * @brief pam_sm_chauthtok API
611 * Function which will be called for pam_chauthtok() calls.
612 *
613 * @param[in] pamh - pam handle
614 * @param[in] flags - pam calls related flags
615 * @param[in] argc - argument counts / options
616 * @param[in] argv - array of arguments / options
617 * @return - PAM_SUCCESS for success, others for failure
618 */
619int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
620{
621 int retval = -1;
622 const void *item = NULL;
623 const char *user = NULL;
624 const char *pass_new = NULL, *pass_old = NULL;
625 const char *spec_grp_name =
626 get_option(pamh, "spec_grp_name", argc, argv);
627 const char *spec_pass_file =
628 get_option(pamh, "spec_pass_file", argc, argv);
629 const char *key_file = get_option(pamh, "key_file", argc, argv);
630
631
632 if (spec_grp_name == NULL || key_file == NULL) {
633 return PAM_IGNORE;
634 }
635 if (flags & PAM_PRELIM_CHECK) {
636 // send success to verify other stacked modules prelim check.
637 return PAM_SUCCESS;
638 }
639
640 retval = pam_get_user(pamh, &user, NULL);
641 if (retval != PAM_SUCCESS) {
642 return retval;
643 }
644
645 // get already read password by the stacked pam module
646 // Note: If there are no previous stacked pam module which read
647 // the new password, then return with AUTHTOK_ERR
648
649 retval = pam_get_item(pamh, PAM_AUTHTOK, &item);
650 if (retval != PAM_SUCCESS || item == NULL) {
651 return PAM_AUTHTOK_ERR;
652 }
653 pass_new = item;
654
655 struct group *grp;
656 int spec_grp_usr = 0;
657 // Verify whether the user belongs to special group.
658 grp = pam_modutil_getgrnam(pamh, spec_grp_name);
659 if (grp != NULL) {
660 while (*(grp->gr_mem) != NULL) {
661 if (strcmp(user, *grp->gr_mem) == 0) {
662 spec_grp_usr = 1;
663 break;
664 }
665 (grp->gr_mem)++;
666 }
667 }
668
669 pam_syslog(pamh, LOG_DEBUG, "User belongs to special grp: %x",
670 spec_grp_usr);
671
672 if (spec_grp_usr) {
673 // verify the new password is acceptable.
674 if (strlen(pass_new) > MAX_SPEC_GRP_PASS_LENGTH
675 || strlen(user) > MAX_SPEC_GRP_USER_LENGTH) {
676 pam_syslog(
677 pamh, LOG_ERR,
678 "Password length (%x) / User name length (%x) not acceptable",
679 strlen(pass_new), strlen(user));
680 pass_new = NULL;
681 return PAM_NEW_AUTHTOK_REQD;
682 }
683 if (spec_pass_file == NULL) {
684 spec_pass_file = DEFAULT_SPEC_PASS_FILE;
685 pam_syslog(
686 pamh, LOG_ERR,
687 "Using default special password file name :%s",
688 spec_pass_file);
689 }
690 if (retval = lock_pwdf()) {
Patrick Venture9565abd2018-11-14 09:11:59 -0800691 pam_syslog(pamh, LOG_ERR,
692 "Failed to lock the passwd file");
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530693 return retval;
694 }
695 retval = update_pass_special_file(
Patrick Venture9565abd2018-11-14 09:11:59 -0800696 pamh, key_file, spec_pass_file, user, pass_new);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530697 unlock_pwdf();
698 return retval;
699 }
700
701 return PAM_SUCCESS;
702}
703
704/* end of module definition */