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