10b0ecb56SDag-Erling Smørgrav /*
2402783abSDag-Erling Smørgrav * Copyright (c) 2000-2002 by Solar Designer. See LICENSE.
30b0ecb56SDag-Erling Smørgrav */
40b0ecb56SDag-Erling Smørgrav
5*caf6fbd8SWill Andrews #define _XOPEN_SOURCE 600
60b0ecb56SDag-Erling Smørgrav #define _XOPEN_SOURCE_EXTENDED
7*caf6fbd8SWill Andrews #define _XOPEN_VERSION 600
80b0ecb56SDag-Erling Smørgrav #include <stdio.h>
90b0ecb56SDag-Erling Smørgrav #include <stdlib.h>
100b0ecb56SDag-Erling Smørgrav #include <stdarg.h>
110b0ecb56SDag-Erling Smørgrav #include <string.h>
120b0ecb56SDag-Erling Smørgrav #include <limits.h>
130b0ecb56SDag-Erling Smørgrav #include <unistd.h>
140b0ecb56SDag-Erling Smørgrav #include <pwd.h>
150b0ecb56SDag-Erling Smørgrav #ifdef HAVE_SHADOW
160b0ecb56SDag-Erling Smørgrav #include <shadow.h>
170b0ecb56SDag-Erling Smørgrav #endif
180b0ecb56SDag-Erling Smørgrav
190b0ecb56SDag-Erling Smørgrav #define PAM_SM_PASSWORD
200b0ecb56SDag-Erling Smørgrav #ifndef LINUX_PAM
210b0ecb56SDag-Erling Smørgrav #include <security/pam_appl.h>
220b0ecb56SDag-Erling Smørgrav #endif
230b0ecb56SDag-Erling Smørgrav #include <security/pam_modules.h>
240b0ecb56SDag-Erling Smørgrav
250b0ecb56SDag-Erling Smørgrav #include "pam_macros.h"
260b0ecb56SDag-Erling Smørgrav
270b0ecb56SDag-Erling Smørgrav #if !defined(PAM_EXTERN) && !defined(PAM_STATIC)
280b0ecb56SDag-Erling Smørgrav #define PAM_EXTERN extern
290b0ecb56SDag-Erling Smørgrav #endif
300b0ecb56SDag-Erling Smørgrav
31402783abSDag-Erling Smørgrav #if !defined(PAM_AUTHTOK_RECOVERY_ERR) && defined(PAM_AUTHTOK_RECOVER_ERR)
32402783abSDag-Erling Smørgrav #define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR
330b0ecb56SDag-Erling Smørgrav #endif
340b0ecb56SDag-Erling Smørgrav
35402783abSDag-Erling Smørgrav #if defined(__sun__) && !defined(LINUX_PAM) && !defined(_OPENPAM)
36402783abSDag-Erling Smørgrav /* Sun's PAM doesn't use const here */
37402783abSDag-Erling Smørgrav #define lo_const
380b0ecb56SDag-Erling Smørgrav #else
39402783abSDag-Erling Smørgrav #define lo_const const
400b0ecb56SDag-Erling Smørgrav #endif
41402783abSDag-Erling Smørgrav typedef lo_const void *pam_item_t;
420b0ecb56SDag-Erling Smørgrav
430b0ecb56SDag-Erling Smørgrav #include "passwdqc.h"
440b0ecb56SDag-Erling Smørgrav
450b0ecb56SDag-Erling Smørgrav #define F_ENFORCE_MASK 0x00000003
460b0ecb56SDag-Erling Smørgrav #define F_ENFORCE_USERS 0x00000001
470b0ecb56SDag-Erling Smørgrav #define F_ENFORCE_ROOT 0x00000002
480b0ecb56SDag-Erling Smørgrav #define F_ENFORCE_EVERYONE F_ENFORCE_MASK
490b0ecb56SDag-Erling Smørgrav #define F_NON_UNIX 0x00000004
500b0ecb56SDag-Erling Smørgrav #define F_ASK_OLDAUTHTOK_MASK 0x00000030
510b0ecb56SDag-Erling Smørgrav #define F_ASK_OLDAUTHTOK_PRELIM 0x00000010
520b0ecb56SDag-Erling Smørgrav #define F_ASK_OLDAUTHTOK_UPDATE 0x00000020
530b0ecb56SDag-Erling Smørgrav #define F_CHECK_OLDAUTHTOK 0x00000040
540b0ecb56SDag-Erling Smørgrav #define F_USE_FIRST_PASS 0x00000100
550b0ecb56SDag-Erling Smørgrav #define F_USE_AUTHTOK 0x00000200
560b0ecb56SDag-Erling Smørgrav
570b0ecb56SDag-Erling Smørgrav typedef struct {
580b0ecb56SDag-Erling Smørgrav passwdqc_params_t qc;
590b0ecb56SDag-Erling Smørgrav int flags;
600b0ecb56SDag-Erling Smørgrav int retry;
610b0ecb56SDag-Erling Smørgrav } params_t;
620b0ecb56SDag-Erling Smørgrav
630b0ecb56SDag-Erling Smørgrav static params_t defaults = {
640b0ecb56SDag-Erling Smørgrav {
650b0ecb56SDag-Erling Smørgrav {INT_MAX, 24, 12, 8, 7}, /* min */
660b0ecb56SDag-Erling Smørgrav 40, /* max */
670b0ecb56SDag-Erling Smørgrav 3, /* passphrase_words */
680b0ecb56SDag-Erling Smørgrav 4, /* match_length */
690b0ecb56SDag-Erling Smørgrav 1, /* similar_deny */
700b0ecb56SDag-Erling Smørgrav 42 /* random_bits */
710b0ecb56SDag-Erling Smørgrav },
720b0ecb56SDag-Erling Smørgrav F_ENFORCE_EVERYONE, /* flags */
730b0ecb56SDag-Erling Smørgrav 3 /* retry */
740b0ecb56SDag-Erling Smørgrav };
750b0ecb56SDag-Erling Smørgrav
760b0ecb56SDag-Erling Smørgrav #define PROMPT_OLDPASS \
770b0ecb56SDag-Erling Smørgrav "Enter current password: "
780b0ecb56SDag-Erling Smørgrav #define PROMPT_NEWPASS1 \
790b0ecb56SDag-Erling Smørgrav "Enter new password: "
800b0ecb56SDag-Erling Smørgrav #define PROMPT_NEWPASS2 \
810b0ecb56SDag-Erling Smørgrav "Re-type new password: "
820b0ecb56SDag-Erling Smørgrav
830b0ecb56SDag-Erling Smørgrav #define MESSAGE_MISCONFIGURED \
840b0ecb56SDag-Erling Smørgrav "System configuration error. Please contact your administrator."
850b0ecb56SDag-Erling Smørgrav #define MESSAGE_INVALID_OPTION \
860b0ecb56SDag-Erling Smørgrav "pam_passwdqc: Invalid option: \"%s\"."
870b0ecb56SDag-Erling Smørgrav #define MESSAGE_INTRO_PASSWORD \
880b0ecb56SDag-Erling Smørgrav "\nYou can now choose the new password.\n"
890b0ecb56SDag-Erling Smørgrav #define MESSAGE_INTRO_BOTH \
900b0ecb56SDag-Erling Smørgrav "\nYou can now choose the new password or passphrase.\n"
910b0ecb56SDag-Erling Smørgrav #define MESSAGE_EXPLAIN_PASSWORD_1 \
920b0ecb56SDag-Erling Smørgrav "A valid password should be a mix of upper and lower case letters,\n" \
930b0ecb56SDag-Erling Smørgrav "digits and other characters. You can use a%s %d character long\n" \
940b0ecb56SDag-Erling Smørgrav "password with characters from at least 3 of these 4 classes.\n" \
950b0ecb56SDag-Erling Smørgrav "Characters that form a common pattern are discarded by the check.\n"
960b0ecb56SDag-Erling Smørgrav #define MESSAGE_EXPLAIN_PASSWORD_2 \
970b0ecb56SDag-Erling Smørgrav "A valid password should be a mix of upper and lower case letters,\n" \
980b0ecb56SDag-Erling Smørgrav "digits and other characters. You can use a%s %d character long\n" \
990b0ecb56SDag-Erling Smørgrav "password with characters from at least 3 of these 4 classes, or\n" \
1000b0ecb56SDag-Erling Smørgrav "a%s %d character long password containing characters from all the\n" \
1010b0ecb56SDag-Erling Smørgrav "classes. Characters that form a common pattern are discarded by\n" \
1020b0ecb56SDag-Erling Smørgrav "the check.\n"
1030b0ecb56SDag-Erling Smørgrav #define MESSAGE_EXPLAIN_PASSPHRASE \
1040b0ecb56SDag-Erling Smørgrav "A passphrase should be of at least %d words, %d to %d characters\n" \
1050b0ecb56SDag-Erling Smørgrav "long and contain enough different characters.\n"
1060b0ecb56SDag-Erling Smørgrav #define MESSAGE_RANDOM \
1070b0ecb56SDag-Erling Smørgrav "Alternatively, if noone else can see your terminal now, you can\n" \
1080b0ecb56SDag-Erling Smørgrav "pick this as your password: \"%s\".\n"
1090b0ecb56SDag-Erling Smørgrav #define MESSAGE_RANDOMONLY \
1100b0ecb56SDag-Erling Smørgrav "This system is configured to permit randomly generated passwords\n" \
1110b0ecb56SDag-Erling Smørgrav "only. If noone else can see your terminal now, you can pick this\n" \
1120b0ecb56SDag-Erling Smørgrav "as your password: \"%s\". Otherwise, come back later.\n"
1130b0ecb56SDag-Erling Smørgrav #define MESSAGE_RANDOMFAILED \
1140b0ecb56SDag-Erling Smørgrav "This system is configured to use randomly generated passwords\n" \
1150b0ecb56SDag-Erling Smørgrav "only, but the attempt to generate a password has failed. This\n" \
1160b0ecb56SDag-Erling Smørgrav "could happen for a number of reasons: you could have requested\n" \
1170b0ecb56SDag-Erling Smørgrav "an impossible password length, or the access to kernel random\n" \
1180b0ecb56SDag-Erling Smørgrav "number pool could have failed."
1190b0ecb56SDag-Erling Smørgrav #define MESSAGE_TOOLONG \
1200b0ecb56SDag-Erling Smørgrav "This password may be too long for some services. Choose another."
1210b0ecb56SDag-Erling Smørgrav #define MESSAGE_TRUNCATED \
1220b0ecb56SDag-Erling Smørgrav "Warning: your longer password will be truncated to 8 characters."
1230b0ecb56SDag-Erling Smørgrav #define MESSAGE_WEAKPASS \
1240b0ecb56SDag-Erling Smørgrav "Weak password: %s."
1250b0ecb56SDag-Erling Smørgrav #define MESSAGE_NOTRANDOM \
1260b0ecb56SDag-Erling Smørgrav "Sorry, you've mistyped the password that was generated for you."
1270b0ecb56SDag-Erling Smørgrav #define MESSAGE_MISTYPED \
1280b0ecb56SDag-Erling Smørgrav "Sorry, passwords do not match."
1290b0ecb56SDag-Erling Smørgrav #define MESSAGE_RETRY \
1300b0ecb56SDag-Erling Smørgrav "Try again."
1310b0ecb56SDag-Erling Smørgrav
converse(pam_handle_t * pamh,int style,lo_const char * text,struct pam_response ** resp)132402783abSDag-Erling Smørgrav static int converse(pam_handle_t *pamh, int style, lo_const char *text,
1330b0ecb56SDag-Erling Smørgrav struct pam_response **resp)
1340b0ecb56SDag-Erling Smørgrav {
135*caf6fbd8SWill Andrews pam_item_t item;
136*caf6fbd8SWill Andrews lo_const struct pam_conv *conv;
1370b0ecb56SDag-Erling Smørgrav struct pam_message msg, *pmsg;
1380b0ecb56SDag-Erling Smørgrav int status;
1390b0ecb56SDag-Erling Smørgrav
140*caf6fbd8SWill Andrews status = pam_get_item(pamh, PAM_CONV, &item);
1410b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS)
1420b0ecb56SDag-Erling Smørgrav return status;
143*caf6fbd8SWill Andrews conv = item;
1440b0ecb56SDag-Erling Smørgrav
1450b0ecb56SDag-Erling Smørgrav pmsg = &msg;
1460b0ecb56SDag-Erling Smørgrav msg.msg_style = style;
147*caf6fbd8SWill Andrews msg.msg = (char *)text;
1480b0ecb56SDag-Erling Smørgrav
1490b0ecb56SDag-Erling Smørgrav *resp = NULL;
150402783abSDag-Erling Smørgrav return conv->conv(1, (lo_const struct pam_message **)&pmsg, resp,
1510b0ecb56SDag-Erling Smørgrav conv->appdata_ptr);
1520b0ecb56SDag-Erling Smørgrav }
1530b0ecb56SDag-Erling Smørgrav
1540b0ecb56SDag-Erling Smørgrav #ifdef __GNUC__
1550b0ecb56SDag-Erling Smørgrav __attribute__ ((format (printf, 3, 4)))
1560b0ecb56SDag-Erling Smørgrav #endif
say(pam_handle_t * pamh,int style,const char * format,...)1570b0ecb56SDag-Erling Smørgrav static int say(pam_handle_t *pamh, int style, const char *format, ...)
1580b0ecb56SDag-Erling Smørgrav {
1590b0ecb56SDag-Erling Smørgrav va_list args;
1600b0ecb56SDag-Erling Smørgrav char buffer[0x800];
1610b0ecb56SDag-Erling Smørgrav int needed;
1620b0ecb56SDag-Erling Smørgrav struct pam_response *resp;
1630b0ecb56SDag-Erling Smørgrav int status;
1640b0ecb56SDag-Erling Smørgrav
1650b0ecb56SDag-Erling Smørgrav va_start(args, format);
1660b0ecb56SDag-Erling Smørgrav needed = vsnprintf(buffer, sizeof(buffer), format, args);
1670b0ecb56SDag-Erling Smørgrav va_end(args);
1680b0ecb56SDag-Erling Smørgrav
169402783abSDag-Erling Smørgrav if ((unsigned int)needed < sizeof(buffer)) {
1700b0ecb56SDag-Erling Smørgrav status = converse(pamh, style, buffer, &resp);
1710b0ecb56SDag-Erling Smørgrav _pam_overwrite(buffer);
1720b0ecb56SDag-Erling Smørgrav } else {
1730b0ecb56SDag-Erling Smørgrav status = PAM_ABORT;
1740b0ecb56SDag-Erling Smørgrav memset(buffer, 0, sizeof(buffer));
1750b0ecb56SDag-Erling Smørgrav }
1760b0ecb56SDag-Erling Smørgrav
1770b0ecb56SDag-Erling Smørgrav return status;
1780b0ecb56SDag-Erling Smørgrav }
1790b0ecb56SDag-Erling Smørgrav
check_max(params_t * params,pam_handle_t * pamh,const char * newpass)180402783abSDag-Erling Smørgrav static int check_max(params_t *params, pam_handle_t *pamh, const char *newpass)
1810b0ecb56SDag-Erling Smørgrav {
182402783abSDag-Erling Smørgrav if ((int)strlen(newpass) > params->qc.max) {
1830b0ecb56SDag-Erling Smørgrav if (params->qc.max != 8) {
1840b0ecb56SDag-Erling Smørgrav say(pamh, PAM_ERROR_MSG, MESSAGE_TOOLONG);
1850b0ecb56SDag-Erling Smørgrav return -1;
1860b0ecb56SDag-Erling Smørgrav }
1870b0ecb56SDag-Erling Smørgrav say(pamh, PAM_TEXT_INFO, MESSAGE_TRUNCATED);
1880b0ecb56SDag-Erling Smørgrav }
1890b0ecb56SDag-Erling Smørgrav
1900b0ecb56SDag-Erling Smørgrav return 0;
1910b0ecb56SDag-Erling Smørgrav }
1920b0ecb56SDag-Erling Smørgrav
parse(params_t * params,pam_handle_t * pamh,int argc,const char ** argv)1930b0ecb56SDag-Erling Smørgrav static int parse(params_t *params, pam_handle_t *pamh,
1940b0ecb56SDag-Erling Smørgrav int argc, const char **argv)
1950b0ecb56SDag-Erling Smørgrav {
1960b0ecb56SDag-Erling Smørgrav const char *p;
197402783abSDag-Erling Smørgrav char *e;
1980b0ecb56SDag-Erling Smørgrav int i;
1990b0ecb56SDag-Erling Smørgrav unsigned long v;
2000b0ecb56SDag-Erling Smørgrav
2010b0ecb56SDag-Erling Smørgrav while (argc) {
2020b0ecb56SDag-Erling Smørgrav if (!strncmp(*argv, "min=", 4)) {
2030b0ecb56SDag-Erling Smørgrav p = *argv + 4;
2040b0ecb56SDag-Erling Smørgrav for (i = 0; i < 5; i++) {
2050b0ecb56SDag-Erling Smørgrav if (!strncmp(p, "disabled", 8)) {
2060b0ecb56SDag-Erling Smørgrav v = INT_MAX;
2070b0ecb56SDag-Erling Smørgrav p += 8;
208402783abSDag-Erling Smørgrav } else {
209402783abSDag-Erling Smørgrav v = strtoul(p, &e, 10);
210402783abSDag-Erling Smørgrav p = e;
211402783abSDag-Erling Smørgrav }
2120b0ecb56SDag-Erling Smørgrav if (i < 4 && *p++ != ',') break;
2130b0ecb56SDag-Erling Smørgrav if (v > INT_MAX) break;
214402783abSDag-Erling Smørgrav if (i && (int)v > params->qc.min[i - 1]) break;
2150b0ecb56SDag-Erling Smørgrav params->qc.min[i] = v;
2160b0ecb56SDag-Erling Smørgrav }
2170b0ecb56SDag-Erling Smørgrav if (*p) break;
2180b0ecb56SDag-Erling Smørgrav } else
2190b0ecb56SDag-Erling Smørgrav if (!strncmp(*argv, "max=", 4)) {
220402783abSDag-Erling Smørgrav v = strtoul(*argv + 4, &e, 10);
221402783abSDag-Erling Smørgrav if (*e || v < 8 || v > INT_MAX) break;
2220b0ecb56SDag-Erling Smørgrav params->qc.max = v;
2230b0ecb56SDag-Erling Smørgrav } else
2240b0ecb56SDag-Erling Smørgrav if (!strncmp(*argv, "passphrase=", 11)) {
225402783abSDag-Erling Smørgrav v = strtoul(*argv + 11, &e, 10);
226402783abSDag-Erling Smørgrav if (*e || v > INT_MAX) break;
2270b0ecb56SDag-Erling Smørgrav params->qc.passphrase_words = v;
2280b0ecb56SDag-Erling Smørgrav } else
2290b0ecb56SDag-Erling Smørgrav if (!strncmp(*argv, "match=", 6)) {
230402783abSDag-Erling Smørgrav v = strtoul(*argv + 6, &e, 10);
231402783abSDag-Erling Smørgrav if (*e || v > INT_MAX) break;
2320b0ecb56SDag-Erling Smørgrav params->qc.match_length = v;
2330b0ecb56SDag-Erling Smørgrav } else
2340b0ecb56SDag-Erling Smørgrav if (!strncmp(*argv, "similar=", 8)) {
2350b0ecb56SDag-Erling Smørgrav if (!strcmp(*argv + 8, "permit"))
2360b0ecb56SDag-Erling Smørgrav params->qc.similar_deny = 0;
2370b0ecb56SDag-Erling Smørgrav else
2380b0ecb56SDag-Erling Smørgrav if (!strcmp(*argv + 8, "deny"))
2390b0ecb56SDag-Erling Smørgrav params->qc.similar_deny = 1;
2400b0ecb56SDag-Erling Smørgrav else
2410b0ecb56SDag-Erling Smørgrav break;
2420b0ecb56SDag-Erling Smørgrav } else
2430b0ecb56SDag-Erling Smørgrav if (!strncmp(*argv, "random=", 7)) {
244402783abSDag-Erling Smørgrav v = strtoul(*argv + 7, &e, 10);
245402783abSDag-Erling Smørgrav if (!strcmp(e, ",only")) {
246402783abSDag-Erling Smørgrav e += 5;
2470b0ecb56SDag-Erling Smørgrav params->qc.min[4] = INT_MAX;
2480b0ecb56SDag-Erling Smørgrav }
249402783abSDag-Erling Smørgrav if (*e || v > INT_MAX) break;
2500b0ecb56SDag-Erling Smørgrav params->qc.random_bits = v;
2510b0ecb56SDag-Erling Smørgrav } else
2520b0ecb56SDag-Erling Smørgrav if (!strncmp(*argv, "enforce=", 8)) {
2530b0ecb56SDag-Erling Smørgrav params->flags &= ~F_ENFORCE_MASK;
2540b0ecb56SDag-Erling Smørgrav if (!strcmp(*argv + 8, "users"))
2550b0ecb56SDag-Erling Smørgrav params->flags |= F_ENFORCE_USERS;
2560b0ecb56SDag-Erling Smørgrav else
2570b0ecb56SDag-Erling Smørgrav if (!strcmp(*argv + 8, "everyone"))
2580b0ecb56SDag-Erling Smørgrav params->flags |= F_ENFORCE_EVERYONE;
2590b0ecb56SDag-Erling Smørgrav else
2600b0ecb56SDag-Erling Smørgrav if (strcmp(*argv + 8, "none"))
2610b0ecb56SDag-Erling Smørgrav break;
2620b0ecb56SDag-Erling Smørgrav } else
2630b0ecb56SDag-Erling Smørgrav if (!strcmp(*argv, "non-unix")) {
2640b0ecb56SDag-Erling Smørgrav if (params->flags & F_CHECK_OLDAUTHTOK) break;
2650b0ecb56SDag-Erling Smørgrav params->flags |= F_NON_UNIX;
2660b0ecb56SDag-Erling Smørgrav } else
2670b0ecb56SDag-Erling Smørgrav if (!strncmp(*argv, "retry=", 6)) {
268402783abSDag-Erling Smørgrav v = strtoul(*argv + 6, &e, 10);
269402783abSDag-Erling Smørgrav if (*e || v > INT_MAX) break;
2700b0ecb56SDag-Erling Smørgrav params->retry = v;
2710b0ecb56SDag-Erling Smørgrav } else
2720b0ecb56SDag-Erling Smørgrav if (!strncmp(*argv, "ask_oldauthtok", 14)) {
2730b0ecb56SDag-Erling Smørgrav params->flags &= ~F_ASK_OLDAUTHTOK_MASK;
2740b0ecb56SDag-Erling Smørgrav if (params->flags & F_USE_FIRST_PASS) break;
2750b0ecb56SDag-Erling Smørgrav if (!strcmp(*argv + 14, "=update"))
2760b0ecb56SDag-Erling Smørgrav params->flags |= F_ASK_OLDAUTHTOK_UPDATE;
2770b0ecb56SDag-Erling Smørgrav else
2780b0ecb56SDag-Erling Smørgrav if (!(*argv)[14])
2790b0ecb56SDag-Erling Smørgrav params->flags |= F_ASK_OLDAUTHTOK_PRELIM;
2800b0ecb56SDag-Erling Smørgrav else
2810b0ecb56SDag-Erling Smørgrav break;
2820b0ecb56SDag-Erling Smørgrav } else
2830b0ecb56SDag-Erling Smørgrav if (!strcmp(*argv, "check_oldauthtok")) {
2840b0ecb56SDag-Erling Smørgrav if (params->flags & F_NON_UNIX) break;
2850b0ecb56SDag-Erling Smørgrav params->flags |= F_CHECK_OLDAUTHTOK;
2860b0ecb56SDag-Erling Smørgrav } else
2870b0ecb56SDag-Erling Smørgrav if (!strcmp(*argv, "use_first_pass")) {
2880b0ecb56SDag-Erling Smørgrav if (params->flags & F_ASK_OLDAUTHTOK_MASK) break;
2890b0ecb56SDag-Erling Smørgrav params->flags |= F_USE_FIRST_PASS | F_USE_AUTHTOK;
2900b0ecb56SDag-Erling Smørgrav } else
2910b0ecb56SDag-Erling Smørgrav if (!strcmp(*argv, "use_authtok")) {
2920b0ecb56SDag-Erling Smørgrav params->flags |= F_USE_AUTHTOK;
2930b0ecb56SDag-Erling Smørgrav } else
2940b0ecb56SDag-Erling Smørgrav break;
2950b0ecb56SDag-Erling Smørgrav argc--; argv++;
2960b0ecb56SDag-Erling Smørgrav }
2970b0ecb56SDag-Erling Smørgrav
2980b0ecb56SDag-Erling Smørgrav if (argc) {
299*caf6fbd8SWill Andrews if (getuid() != 0) {
300*caf6fbd8SWill Andrews say(pamh, PAM_ERROR_MSG, MESSAGE_MISCONFIGURED);
301*caf6fbd8SWill Andrews } else {
302*caf6fbd8SWill Andrews say(pamh, PAM_ERROR_MSG, MESSAGE_INVALID_OPTION, *argv);
303*caf6fbd8SWill Andrews }
3040b0ecb56SDag-Erling Smørgrav return PAM_ABORT;
3050b0ecb56SDag-Erling Smørgrav }
3060b0ecb56SDag-Erling Smørgrav
3070b0ecb56SDag-Erling Smørgrav return PAM_SUCCESS;
3080b0ecb56SDag-Erling Smørgrav }
3090b0ecb56SDag-Erling Smørgrav
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)3100b0ecb56SDag-Erling Smørgrav PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
3110b0ecb56SDag-Erling Smørgrav int argc, const char **argv)
3120b0ecb56SDag-Erling Smørgrav {
3130b0ecb56SDag-Erling Smørgrav params_t params;
3140b0ecb56SDag-Erling Smørgrav struct pam_response *resp;
3150b0ecb56SDag-Erling Smørgrav struct passwd *pw, fake_pw;
3160b0ecb56SDag-Erling Smørgrav #ifdef HAVE_SHADOW
3170b0ecb56SDag-Erling Smørgrav struct spwd *spw;
3180b0ecb56SDag-Erling Smørgrav #endif
319*caf6fbd8SWill Andrews pam_item_t item;
320*caf6fbd8SWill Andrews lo_const char *user, *oldpass, *curpass;
321*caf6fbd8SWill Andrews char *newpass, *randompass;
322402783abSDag-Erling Smørgrav const char *reason;
3230b0ecb56SDag-Erling Smørgrav int ask_oldauthtok;
3240b0ecb56SDag-Erling Smørgrav int randomonly, enforce, retries_left, retry_wanted;
3250b0ecb56SDag-Erling Smørgrav int status;
3260b0ecb56SDag-Erling Smørgrav
3270b0ecb56SDag-Erling Smørgrav params = defaults;
3280b0ecb56SDag-Erling Smørgrav status = parse(¶ms, pamh, argc, argv);
3290b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS)
3300b0ecb56SDag-Erling Smørgrav return status;
3310b0ecb56SDag-Erling Smørgrav
3320b0ecb56SDag-Erling Smørgrav ask_oldauthtok = 0;
3330b0ecb56SDag-Erling Smørgrav if (flags & PAM_PRELIM_CHECK) {
3340b0ecb56SDag-Erling Smørgrav if (params.flags & F_ASK_OLDAUTHTOK_PRELIM)
3350b0ecb56SDag-Erling Smørgrav ask_oldauthtok = 1;
3360b0ecb56SDag-Erling Smørgrav } else
3370b0ecb56SDag-Erling Smørgrav if (flags & PAM_UPDATE_AUTHTOK) {
3380b0ecb56SDag-Erling Smørgrav if (params.flags & F_ASK_OLDAUTHTOK_UPDATE)
3390b0ecb56SDag-Erling Smørgrav ask_oldauthtok = 1;
3400b0ecb56SDag-Erling Smørgrav } else
3410b0ecb56SDag-Erling Smørgrav return PAM_SERVICE_ERR;
3420b0ecb56SDag-Erling Smørgrav
3430b0ecb56SDag-Erling Smørgrav if (ask_oldauthtok && getuid() != 0) {
3440b0ecb56SDag-Erling Smørgrav status = converse(pamh, PAM_PROMPT_ECHO_OFF,
3450b0ecb56SDag-Erling Smørgrav PROMPT_OLDPASS, &resp);
3460b0ecb56SDag-Erling Smørgrav
3470b0ecb56SDag-Erling Smørgrav if (status == PAM_SUCCESS) {
3480b0ecb56SDag-Erling Smørgrav if (resp && resp->resp) {
3490b0ecb56SDag-Erling Smørgrav status = pam_set_item(pamh,
3500b0ecb56SDag-Erling Smørgrav PAM_OLDAUTHTOK, resp->resp);
3510b0ecb56SDag-Erling Smørgrav _pam_drop_reply(resp, 1);
3520b0ecb56SDag-Erling Smørgrav } else
353402783abSDag-Erling Smørgrav status = PAM_AUTHTOK_RECOVERY_ERR;
3540b0ecb56SDag-Erling Smørgrav }
3550b0ecb56SDag-Erling Smørgrav
3560b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS)
3570b0ecb56SDag-Erling Smørgrav return status;
3580b0ecb56SDag-Erling Smørgrav }
3590b0ecb56SDag-Erling Smørgrav
3600b0ecb56SDag-Erling Smørgrav if (flags & PAM_PRELIM_CHECK)
3610b0ecb56SDag-Erling Smørgrav return status;
3620b0ecb56SDag-Erling Smørgrav
363*caf6fbd8SWill Andrews status = pam_get_item(pamh, PAM_USER, &item);
3640b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS)
3650b0ecb56SDag-Erling Smørgrav return status;
366*caf6fbd8SWill Andrews user = item;
3670b0ecb56SDag-Erling Smørgrav
368*caf6fbd8SWill Andrews status = pam_get_item(pamh, PAM_OLDAUTHTOK, &item);
3690b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS)
3700b0ecb56SDag-Erling Smørgrav return status;
371*caf6fbd8SWill Andrews oldpass = item;
3720b0ecb56SDag-Erling Smørgrav
3730b0ecb56SDag-Erling Smørgrav if (params.flags & F_NON_UNIX) {
3740b0ecb56SDag-Erling Smørgrav pw = &fake_pw;
375*caf6fbd8SWill Andrews pw->pw_name = (char *)user;
3760b0ecb56SDag-Erling Smørgrav pw->pw_gecos = "";
3770b0ecb56SDag-Erling Smørgrav } else {
3780b0ecb56SDag-Erling Smørgrav pw = getpwnam(user);
3790b0ecb56SDag-Erling Smørgrav endpwent();
3800b0ecb56SDag-Erling Smørgrav if (!pw)
3810b0ecb56SDag-Erling Smørgrav return PAM_USER_UNKNOWN;
3820b0ecb56SDag-Erling Smørgrav if ((params.flags & F_CHECK_OLDAUTHTOK) && getuid() != 0) {
3830b0ecb56SDag-Erling Smørgrav if (!oldpass)
3840b0ecb56SDag-Erling Smørgrav status = PAM_AUTH_ERR;
3850b0ecb56SDag-Erling Smørgrav else
3860b0ecb56SDag-Erling Smørgrav #ifdef HAVE_SHADOW
3870b0ecb56SDag-Erling Smørgrav if (!strcmp(pw->pw_passwd, "x")) {
3880b0ecb56SDag-Erling Smørgrav spw = getspnam(user);
3890b0ecb56SDag-Erling Smørgrav endspent();
3900b0ecb56SDag-Erling Smørgrav if (spw) {
3910b0ecb56SDag-Erling Smørgrav if (strcmp(crypt(oldpass, spw->sp_pwdp),
3920b0ecb56SDag-Erling Smørgrav spw->sp_pwdp))
3930b0ecb56SDag-Erling Smørgrav status = PAM_AUTH_ERR;
3940b0ecb56SDag-Erling Smørgrav memset(spw->sp_pwdp, 0,
3950b0ecb56SDag-Erling Smørgrav strlen(spw->sp_pwdp));
3960b0ecb56SDag-Erling Smørgrav } else
3970b0ecb56SDag-Erling Smørgrav status = PAM_AUTH_ERR;
3980b0ecb56SDag-Erling Smørgrav } else
3990b0ecb56SDag-Erling Smørgrav #endif
4000b0ecb56SDag-Erling Smørgrav if (strcmp(crypt(oldpass, pw->pw_passwd),
4010b0ecb56SDag-Erling Smørgrav pw->pw_passwd))
4020b0ecb56SDag-Erling Smørgrav status = PAM_AUTH_ERR;
4030b0ecb56SDag-Erling Smørgrav }
4040b0ecb56SDag-Erling Smørgrav memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
4050b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS)
4060b0ecb56SDag-Erling Smørgrav return status;
4070b0ecb56SDag-Erling Smørgrav }
4080b0ecb56SDag-Erling Smørgrav
4090b0ecb56SDag-Erling Smørgrav randomonly = params.qc.min[4] > params.qc.max;
4100b0ecb56SDag-Erling Smørgrav
4110b0ecb56SDag-Erling Smørgrav if (getuid() != 0)
4120b0ecb56SDag-Erling Smørgrav enforce = params.flags & F_ENFORCE_USERS;
4130b0ecb56SDag-Erling Smørgrav else
4140b0ecb56SDag-Erling Smørgrav enforce = params.flags & F_ENFORCE_ROOT;
4150b0ecb56SDag-Erling Smørgrav
4160b0ecb56SDag-Erling Smørgrav if (params.flags & F_USE_AUTHTOK) {
417*caf6fbd8SWill Andrews status = pam_get_item(pamh, PAM_AUTHTOK, &item);
4180b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS)
4190b0ecb56SDag-Erling Smørgrav return status;
420*caf6fbd8SWill Andrews curpass = item;
421*caf6fbd8SWill Andrews if (!curpass || (check_max(¶ms, pamh, curpass) && enforce))
42200d65bdcSDag-Erling Smørgrav return PAM_AUTHTOK_ERR;
423*caf6fbd8SWill Andrews reason = _passwdqc_check(¶ms.qc, curpass, oldpass, pw);
4240b0ecb56SDag-Erling Smørgrav if (reason) {
4250b0ecb56SDag-Erling Smørgrav say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason);
4260b0ecb56SDag-Erling Smørgrav if (enforce)
42700d65bdcSDag-Erling Smørgrav status = PAM_AUTHTOK_ERR;
4280b0ecb56SDag-Erling Smørgrav }
4290b0ecb56SDag-Erling Smørgrav return status;
4300b0ecb56SDag-Erling Smørgrav }
4310b0ecb56SDag-Erling Smørgrav
4320b0ecb56SDag-Erling Smørgrav retries_left = params.retry;
4330b0ecb56SDag-Erling Smørgrav
4340b0ecb56SDag-Erling Smørgrav retry:
4350b0ecb56SDag-Erling Smørgrav retry_wanted = 0;
4360b0ecb56SDag-Erling Smørgrav
4370b0ecb56SDag-Erling Smørgrav if (!randomonly &&
4380b0ecb56SDag-Erling Smørgrav params.qc.passphrase_words && params.qc.min[2] <= params.qc.max)
4390b0ecb56SDag-Erling Smørgrav status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_BOTH);
4400b0ecb56SDag-Erling Smørgrav else
4410b0ecb56SDag-Erling Smørgrav status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_PASSWORD);
4420b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS)
4430b0ecb56SDag-Erling Smørgrav return status;
4440b0ecb56SDag-Erling Smørgrav
4450b0ecb56SDag-Erling Smørgrav if (!randomonly && params.qc.min[3] <= params.qc.min[4])
4460b0ecb56SDag-Erling Smørgrav status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_1,
4470b0ecb56SDag-Erling Smørgrav params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "",
4480b0ecb56SDag-Erling Smørgrav params.qc.min[3]);
4490b0ecb56SDag-Erling Smørgrav else
4500b0ecb56SDag-Erling Smørgrav if (!randomonly)
4510b0ecb56SDag-Erling Smørgrav status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_2,
4520b0ecb56SDag-Erling Smørgrav params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "",
4530b0ecb56SDag-Erling Smørgrav params.qc.min[3],
4540b0ecb56SDag-Erling Smørgrav params.qc.min[4] == 8 || params.qc.min[4] == 11 ? "n" : "",
4550b0ecb56SDag-Erling Smørgrav params.qc.min[4]);
4560b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS)
4570b0ecb56SDag-Erling Smørgrav return status;
4580b0ecb56SDag-Erling Smørgrav
4590b0ecb56SDag-Erling Smørgrav if (!randomonly &&
4600b0ecb56SDag-Erling Smørgrav params.qc.passphrase_words &&
4610b0ecb56SDag-Erling Smørgrav params.qc.min[2] <= params.qc.max) {
4620b0ecb56SDag-Erling Smørgrav status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSPHRASE,
4630b0ecb56SDag-Erling Smørgrav params.qc.passphrase_words,
4640b0ecb56SDag-Erling Smørgrav params.qc.min[2], params.qc.max);
4650b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS)
4660b0ecb56SDag-Erling Smørgrav return status;
4670b0ecb56SDag-Erling Smørgrav }
4680b0ecb56SDag-Erling Smørgrav
4690b0ecb56SDag-Erling Smørgrav randompass = _passwdqc_random(¶ms.qc);
4700b0ecb56SDag-Erling Smørgrav if (randompass) {
4710b0ecb56SDag-Erling Smørgrav status = say(pamh, PAM_TEXT_INFO, randomonly ?
4720b0ecb56SDag-Erling Smørgrav MESSAGE_RANDOMONLY : MESSAGE_RANDOM, randompass);
4730b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS) {
4740b0ecb56SDag-Erling Smørgrav _pam_overwrite(randompass);
4750b0ecb56SDag-Erling Smørgrav randompass = NULL;
4760b0ecb56SDag-Erling Smørgrav }
4770b0ecb56SDag-Erling Smørgrav } else
4780b0ecb56SDag-Erling Smørgrav if (randomonly) {
4790b0ecb56SDag-Erling Smørgrav say(pamh, PAM_ERROR_MSG, getuid() != 0 ?
4800b0ecb56SDag-Erling Smørgrav MESSAGE_MISCONFIGURED : MESSAGE_RANDOMFAILED);
48100d65bdcSDag-Erling Smørgrav return PAM_AUTHTOK_ERR;
4820b0ecb56SDag-Erling Smørgrav }
4830b0ecb56SDag-Erling Smørgrav
4840b0ecb56SDag-Erling Smørgrav status = converse(pamh, PAM_PROMPT_ECHO_OFF, PROMPT_NEWPASS1, &resp);
4850b0ecb56SDag-Erling Smørgrav if (status == PAM_SUCCESS && (!resp || !resp->resp))
48600d65bdcSDag-Erling Smørgrav status = PAM_AUTHTOK_ERR;
4870b0ecb56SDag-Erling Smørgrav
4880b0ecb56SDag-Erling Smørgrav if (status != PAM_SUCCESS) {
4890b0ecb56SDag-Erling Smørgrav if (randompass) _pam_overwrite(randompass);
4900b0ecb56SDag-Erling Smørgrav return status;
4910b0ecb56SDag-Erling Smørgrav }
4920b0ecb56SDag-Erling Smørgrav
4930b0ecb56SDag-Erling Smørgrav newpass = strdup(resp->resp);
4940b0ecb56SDag-Erling Smørgrav
4950b0ecb56SDag-Erling Smørgrav _pam_drop_reply(resp, 1);
4960b0ecb56SDag-Erling Smørgrav
4970b0ecb56SDag-Erling Smørgrav if (!newpass) {
4980b0ecb56SDag-Erling Smørgrav if (randompass) _pam_overwrite(randompass);
499*caf6fbd8SWill Andrews return status;
5000b0ecb56SDag-Erling Smørgrav }
5010b0ecb56SDag-Erling Smørgrav
5020b0ecb56SDag-Erling Smørgrav if (check_max(¶ms, pamh, newpass) && enforce) {
50300d65bdcSDag-Erling Smørgrav status = PAM_AUTHTOK_ERR;
5040b0ecb56SDag-Erling Smørgrav retry_wanted = 1;
5050b0ecb56SDag-Erling Smørgrav }
5060b0ecb56SDag-Erling Smørgrav
5070b0ecb56SDag-Erling Smørgrav reason = NULL;
5080b0ecb56SDag-Erling Smørgrav if (status == PAM_SUCCESS &&
5090b0ecb56SDag-Erling Smørgrav (!randompass || !strstr(newpass, randompass)) &&
5100b0ecb56SDag-Erling Smørgrav (randomonly ||
5110b0ecb56SDag-Erling Smørgrav (reason = _passwdqc_check(¶ms.qc, newpass, oldpass, pw)))) {
5120b0ecb56SDag-Erling Smørgrav if (randomonly)
5130b0ecb56SDag-Erling Smørgrav say(pamh, PAM_ERROR_MSG, MESSAGE_NOTRANDOM);
5140b0ecb56SDag-Erling Smørgrav else
5150b0ecb56SDag-Erling Smørgrav say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason);
5160b0ecb56SDag-Erling Smørgrav if (enforce) {
51700d65bdcSDag-Erling Smørgrav status = PAM_AUTHTOK_ERR;
5180b0ecb56SDag-Erling Smørgrav retry_wanted = 1;
5190b0ecb56SDag-Erling Smørgrav }
5200b0ecb56SDag-Erling Smørgrav }
5210b0ecb56SDag-Erling Smørgrav
5220b0ecb56SDag-Erling Smørgrav if (status == PAM_SUCCESS)
5230b0ecb56SDag-Erling Smørgrav status = converse(pamh, PAM_PROMPT_ECHO_OFF,
5240b0ecb56SDag-Erling Smørgrav PROMPT_NEWPASS2, &resp);
5250b0ecb56SDag-Erling Smørgrav if (status == PAM_SUCCESS) {
5260b0ecb56SDag-Erling Smørgrav if (resp && resp->resp) {
5270b0ecb56SDag-Erling Smørgrav if (strcmp(newpass, resp->resp)) {
5280b0ecb56SDag-Erling Smørgrav status = say(pamh,
5290b0ecb56SDag-Erling Smørgrav PAM_ERROR_MSG, MESSAGE_MISTYPED);
5300b0ecb56SDag-Erling Smørgrav if (status == PAM_SUCCESS) {
53100d65bdcSDag-Erling Smørgrav status = PAM_AUTHTOK_ERR;
5320b0ecb56SDag-Erling Smørgrav retry_wanted = 1;
5330b0ecb56SDag-Erling Smørgrav }
5340b0ecb56SDag-Erling Smørgrav }
5350b0ecb56SDag-Erling Smørgrav _pam_drop_reply(resp, 1);
5360b0ecb56SDag-Erling Smørgrav } else
53700d65bdcSDag-Erling Smørgrav status = PAM_AUTHTOK_ERR;
5380b0ecb56SDag-Erling Smørgrav }
5390b0ecb56SDag-Erling Smørgrav
5400b0ecb56SDag-Erling Smørgrav if (status == PAM_SUCCESS)
5410b0ecb56SDag-Erling Smørgrav status = pam_set_item(pamh, PAM_AUTHTOK, newpass);
5420b0ecb56SDag-Erling Smørgrav
5430b0ecb56SDag-Erling Smørgrav if (randompass) _pam_overwrite(randompass);
5440b0ecb56SDag-Erling Smørgrav _pam_overwrite(newpass);
5450b0ecb56SDag-Erling Smørgrav free(newpass);
5460b0ecb56SDag-Erling Smørgrav
5470b0ecb56SDag-Erling Smørgrav if (retry_wanted && --retries_left > 0) {
5480b0ecb56SDag-Erling Smørgrav status = say(pamh, PAM_TEXT_INFO, MESSAGE_RETRY);
5490b0ecb56SDag-Erling Smørgrav if (status == PAM_SUCCESS)
5500b0ecb56SDag-Erling Smørgrav goto retry;
5510b0ecb56SDag-Erling Smørgrav }
5520b0ecb56SDag-Erling Smørgrav
5530b0ecb56SDag-Erling Smørgrav return status;
5540b0ecb56SDag-Erling Smørgrav }
5550b0ecb56SDag-Erling Smørgrav
556402783abSDag-Erling Smørgrav #ifdef PAM_MODULE_ENTRY
557402783abSDag-Erling Smørgrav PAM_MODULE_ENTRY("pam_passwdqc");
558402783abSDag-Erling Smørgrav #elif defined(PAM_STATIC)
5590b0ecb56SDag-Erling Smørgrav struct pam_module _pam_passwdqc_modstruct = {
5600b0ecb56SDag-Erling Smørgrav "pam_passwdqc",
5610b0ecb56SDag-Erling Smørgrav NULL,
5620b0ecb56SDag-Erling Smørgrav NULL,
5630b0ecb56SDag-Erling Smørgrav NULL,
5640b0ecb56SDag-Erling Smørgrav NULL,
5650b0ecb56SDag-Erling Smørgrav NULL,
5660b0ecb56SDag-Erling Smørgrav pam_sm_chauthtok
5670b0ecb56SDag-Erling Smørgrav };
5680b0ecb56SDag-Erling Smørgrav #endif
569