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