xref: /openbmc/pam-ipmi/src/pam_ipmisave/pam_ipmisave.c (revision d9c11abd92db59a4559c71eadbb8e07fcf09dcd4)
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