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