blob: eec1f72ed5f13fb105c4a38b07ea145e11f8c0aa [file] [log] [blame]
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +05301/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Patrick Williams9e7627a2023-05-10 07:51:06 -050017#include <fcntl.h>
18#include <stdarg.h>
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +053019#include <stdio.h>
20#include <stdlib.h>
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +053021#include <string.h>
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +053022#include <sys/stat.h>
Patrick Williams9e7627a2023-05-10 07:51:06 -050023#include <syslog.h>
24#include <unistd.h>
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +053025
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +053026#include <security/pam_ext.h>
Patrick Williams9e7627a2023-05-10 07:51:06 -050027#include <security/pam_modules.h>
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +053028#include <security/pam_modutil.h>
29
30#include <openssl/evp.h>
31#include <openssl/hmac.h>
32#include <openssl/rand.h>
33
34/*
35 * This module is intended to save password of special group user
36 *
37 */
38
39#define MAX_SPEC_GRP_PASS_LENGTH 20
40#define MAX_SPEC_GRP_USER_LENGTH 16
41#define MAX_KEY_SIZE 8
42#define DEFAULT_SPEC_PASS_FILE "/etc/ipmi_pass"
43#define META_PASSWD_SIG "=OPENBMC="
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +053044
45/*
46 * Meta data struct for storing the encrypted password file
47 * Note: Followed by this structure, the real data of hash, iv, encrypted data
48 * with pad and mac are stored.
49 * Decrypted data will hold user name & password for every new line with format
50 * like <user name>:<password>\n
51 */
52typedef struct metapassstruct {
53 char signature[10];
54 unsigned char reseved[2];
55 size_t hashsize;
56 size_t ivsize;
57 size_t datasize;
58 size_t padsize;
59 size_t macsize;
60} metapassstruct;
61
62/**
63 * @brief to acquire lock for atomic operation
64 * Internally uses lckpwdf to acquire the lock. Tries to acquire the lock
65 * using lckpwdf() in interval of 1ms, with maximum of 100 attempts.
66 *
67 * @return PAM_SUCCESS for success / PAM_AUTHTOK_LOCK_BUSY for failure
68 */
69int lock_pwdf(void)
70{
71 int i;
72 int retval;
73
74 i = 0;
75 while ((retval = lckpwdf()) != 0 && i < 100) {
76 usleep(1000);
77 i++;
78 }
79 if (retval != 0) {
80 return PAM_AUTHTOK_LOCK_BUSY;
81 }
82 return PAM_SUCCESS;
83}
84
85/**
86 * @brief unlock the acquired lock
87 * Internally uses ulckpwdf to release the lock
88 */
89void unlock_pwdf(void)
90{
91 ulckpwdf();
92}
93
94/**
95 * @brief to get argument value of option
96 * Function to get the value of argument options.
97 *
98 * @param[in] pamh - pam handle
99 * @param[in] option - argument option to which value has to returned
100 * @param[in] argc - argument count
101 * @param[in] argv - array of arguments
102 */
103static const char *get_option(const pam_handle_t *pamh, const char *option,
104 int argc, const char **argv)
105{
106 int i;
107 size_t len;
108
109 len = strlen(option);
110
111 for (i = 0; i < argc; ++i) {
112 if (strncmp(option, argv[i], len) == 0) {
113 if (argv[i][len] == '=') {
114 return &argv[i][len + 1];
115 }
116 }
117 }
118 return NULL;
119}
120
121/**
122 * @brief encrypt or decrypt function
123 * Function which will do the encryption or decryption of the data.
124 *
125 * @param[in] pamh - pam handle.
126 * @param[in] isencrypt - encrypt or decrypt option.
127 * @param[in] cipher - EVP_CIPHER to be used
128 * @param[in] key - key which has to be used in EVP_CIPHER api's.
129 * @param[in] keylen - Length of the key.
130 * @param[in] iv - Initialization vector data, used along with key
131 * @param[in] ivlen - Length of IV.
132 * @param[in] inbytes - buffer which has to be encrypted or decrypted.
133 * @param[in] inbyteslen - length of input buffer.
134 * @param[in] outbytes - buffer to store decrypted or encrypted data.
135 * @param[in] outbyteslen - length of output buffer
136 * @param[in/out] mac - checksum to cross verify. Will be verified for decrypt
137 * and returns for encrypt.
138 * @param[in/out] maclen - length of checksum
139 * @return - 0 for success -1 for failures.
140 */
141int encrypt_decrypt_data(const pam_handle_t *pamh, int isencrypt,
142 const EVP_CIPHER *cipher, const char *key,
143 size_t keylen, const char *iv, size_t ivlen,
144 const char *inbytes, size_t inbyteslen, char *outbytes,
145 size_t *outbyteslen, char *mac, size_t *maclen)
146{
147 EVP_CIPHER_CTX *ctx;
148 const EVP_MD *digest;
149 size_t outEVPlen = 0;
150 int retval = 0;
151 size_t outlen = 0;
152
Patrick Williams9e7627a2023-05-10 07:51:06 -0500153 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) {
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530157 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.
Patrick Williams9e7627a2023-05-10 07:51:06 -0500166 if (NULL == HMAC(digest, key, keylen, inbytes, inbyteslen,
167 calmac, &calmaclen)) {
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530168 pam_syslog(pamh, LOG_DEBUG,
169 "Failed to verify authentication %d",
170 retval);
171 return -1;
172 }
Patrick Williams9e7627a2023-05-10 07:51:06 -0500173 if (!((calmaclen == *maclen) &&
174 (memcmp(calmac, mac, calmaclen) == 0))) {
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530175 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;
Patrick Williams9e7627a2023-05-10 07:51:06 -0500196 if ((retval =
197 EVP_CipherFinal(ctx, outbytes + outlen, &outEVPlen))) {
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530198 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.
Patrick Williams9e7627a2023-05-10 07:51:06 -0500216 if (NULL == HMAC(digest, key, keylen, outbytes, *outbyteslen,
217 mac, maclen)) {
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530218 pam_syslog(pamh, LOG_DEBUG,
219 "Failed to create authentication %d",
220 retval);
221 return -1;
222 }
223 }
224 return 0;
225}
226
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530227/**
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 */
Patrick Venture9565abd2018-11-14 09:11:59 -0800235FILE *get_temp_file_handle(const pam_handle_t *pamh, char *const tempfilename)
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530236{
237 FILE *tempfile = NULL;
Patrick Ventured0e324a2018-11-14 10:18:50 -0800238 int fd;
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530239 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
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530253/**
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 */
265int 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
Patrick Venture9565abd2018-11-14 09:11:59 -0800296 // 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
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530300 // 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).
Patrick Williams9e7627a2023-05-10 07:51:06 -0500306 if (strlen(filename) >
307 (sizeof(tempfilename) - strlen("__XXXXXX") - 1)) {
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530308 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
Patrick Venture9565abd2018-11-14 09:11:59 -0800325 // 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.
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530328 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));
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530346 }
Vernon Maueryf3919c42020-04-08 17:05:57 -0700347 // Override the file permission with S_IWUSR | S_IRUSR
348 st.st_mode = S_IWUSR | S_IRUSR;
Patrick Williams9e7627a2023-05-10 07:51:06 -0500349 if ((fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) ||
350 (fchmod(fileno(pwfile), st.st_mode) == -1)) {
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530351 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 }
Patrick Venture9565abd2018-11-14 09:11:59 -0800380 // User & password pairs are mapped as <user
381 // name>:<password>\n. Add +3 for special chars ':',
382 // '\n' and '\0'.
Patrick Williams9e7627a2023-05-10 07:51:06 -0500383 pwptextlen = opwmp->datasize + forwholen + towhatlen +
384 3 + EVP_CIPHER_block_size(cipher);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530385 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(
Patrick Williams9e7627a2023-05-10 07:51:06 -0500404 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 +
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530414 opwmp->datasize + opwmp->padsize,
Patrick Williams9e7627a2023-05-10 07:51:06 -0500415 &opwmp->macsize) != 0) {
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530416 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) {
Patrick Williams9e7627a2023-05-10 07:51:06 -0500436 if ((!strncmp(linebuff, forwho, forwholen)) &&
437 (linebuff[forwholen] == ':')) {
438 writtensize +=
439 snprintf(pwptext + writtensize,
440 pwptextlen - writtensize,
441 "%s:%s\n", forwho, towhat);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530442 wroteentry = 1;
443 } else {
Patrick Williams9e7627a2023-05-10 07:51:06 -0500444 writtensize +=
445 snprintf(pwptext + writtensize,
446 pwptextlen - writtensize,
447 "%s\n", linebuff);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530448 }
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 {
Patrick Williams9e7627a2023-05-10 07:51:06 -0500457 pwptextlen =
458 forwholen + towhatlen + 3 + EVP_CIPHER_block_size(cipher);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530459 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
Richard Marian Thomaiyar65edb932019-01-28 20:39:10 +0530474 if (!wroteentry) {
475 // Write the new user:password pair at the end.
Patrick Williams9e7627a2023-05-10 07:51:06 -0500476 writtensize +=
477 snprintf(pwptext + writtensize, pwptextlen - writtensize,
478 "%s:%s\n", forwho, towhat);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530479 }
Richard Marian Thomaiyar65edb932019-01-28 20:39:10 +0530480 pwptextlen = writtensize;
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530481
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
Patrick Williams9e7627a2023-05-10 07:51:06 -0500516 if (encrypt_decrypt_data(
517 pamh, 1, cipher, key, keylen, iv, EVP_CIPHER_iv_length(cipher),
518 pwptext, pwptextlen, pwctext, &pwctextlen, mac, &maclen) != 0) {
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530519 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(
Patrick Williams9e7627a2023-05-10 07:51:06 -0500562 pamh, LOG_DEBUG,
563 "fflush or fsync error writing entries to special file: %s",
564 tempfilename);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530565 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
575done:
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
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530598/* 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 */
610int 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 =
Patrick Williams9e7627a2023-05-10 07:51:06 -0500617 get_option(pamh, "spec_grp_name", argc, argv);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530618 const char *spec_pass_file =
Patrick Williams9e7627a2023-05-10 07:51:06 -0500619 get_option(pamh, "spec_pass_file", argc, argv);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530620 const char *key_file = get_option(pamh, "key_file", argc, argv);
621
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530622 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.
Jiaqing Zhaoa80864a2022-04-04 16:36:35 +0800664 size_t pass_len = strlen(pass_new);
665 size_t user_len = strlen(user);
Patrick Williams9e7627a2023-05-10 07:51:06 -0500666 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);
Jiaqing Zhaoa80864a2022-04-04 16:36:35 +0800672 pass_new = pass_old = NULL;
Jiaqing Zhaoe3771e82022-04-02 18:02:25 +0800673 return PAM_AUTHTOK_ERR;
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530674 }
675 if (spec_pass_file == NULL) {
676 spec_pass_file = DEFAULT_SPEC_PASS_FILE;
677 pam_syslog(
Patrick Williams9e7627a2023-05-10 07:51:06 -0500678 pamh, LOG_ERR,
679 "Using default special password file name :%s",
680 spec_pass_file);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530681 }
682 if (retval = lock_pwdf()) {
Patrick Venture9565abd2018-11-14 09:11:59 -0800683 pam_syslog(pamh, LOG_ERR,
684 "Failed to lock the passwd file");
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530685 return retval;
686 }
687 retval = update_pass_special_file(
Patrick Williams9e7627a2023-05-10 07:51:06 -0500688 pamh, key_file, spec_pass_file, user, pass_new);
Richard Marian Thomaiyar216f2132018-06-12 19:20:48 +0530689 unlock_pwdf();
690 return retval;
691 }
692
693 return PAM_SUCCESS;
694}
695
696/* end of module definition */