2 * Copyright (c) 2000-2002 by Solar Designer. See LICENSE.
5 #define _XOPEN_SOURCE 500
6 #define _XOPEN_SOURCE_EXTENDED
7 #define _XOPEN_VERSION 500
19 #define PAM_SM_PASSWORD
21 #include <security/pam_appl.h>
23 #include <security/pam_modules.h>
25 #include "pam_macros.h"
27 #if !defined(PAM_EXTERN) && !defined(PAM_STATIC)
28 #define PAM_EXTERN extern
31 #if !defined(PAM_AUTHTOK_RECOVERY_ERR) && defined(PAM_AUTHTOK_RECOVER_ERR)
32 #define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR
35 #if defined(__sun__) && !defined(LINUX_PAM) && !defined(_OPENPAM)
36 /* Sun's PAM doesn't use const here */
39 #define lo_const const
41 typedef lo_const
void *pam_item_t
;
45 #define F_ENFORCE_MASK 0x00000003
46 #define F_ENFORCE_USERS 0x00000001
47 #define F_ENFORCE_ROOT 0x00000002
48 #define F_ENFORCE_EVERYONE F_ENFORCE_MASK
49 #define F_NON_UNIX 0x00000004
50 #define F_ASK_OLDAUTHTOK_MASK 0x00000030
51 #define F_ASK_OLDAUTHTOK_PRELIM 0x00000010
52 #define F_ASK_OLDAUTHTOK_UPDATE 0x00000020
53 #define F_CHECK_OLDAUTHTOK 0x00000040
54 #define F_USE_FIRST_PASS 0x00000100
55 #define F_USE_AUTHTOK 0x00000200
63 static params_t defaults
= {
65 {INT_MAX
, 24, 12, 8, 7}, /* min */
67 3, /* passphrase_words */
72 F_ENFORCE_EVERYONE
, /* flags */
76 #define PROMPT_OLDPASS \
77 "Enter current password: "
78 #define PROMPT_NEWPASS1 \
79 "Enter new password: "
80 #define PROMPT_NEWPASS2 \
81 "Re-type new password: "
83 #define MESSAGE_MISCONFIGURED \
84 "System configuration error. Please contact your administrator."
85 #define MESSAGE_INVALID_OPTION \
86 "pam_passwdqc: Invalid option: \"%s\"."
87 #define MESSAGE_INTRO_PASSWORD \
88 "\nYou can now choose the new password.\n"
89 #define MESSAGE_INTRO_BOTH \
90 "\nYou can now choose the new password or passphrase.\n"
91 #define MESSAGE_EXPLAIN_PASSWORD_1 \
92 "A valid password should be a mix of upper and lower case letters,\n" \
93 "digits and other characters. You can use a%s %d character long\n" \
94 "password with characters from at least 3 of these 4 classes.\n" \
95 "Characters that form a common pattern are discarded by the check.\n"
96 #define MESSAGE_EXPLAIN_PASSWORD_2 \
97 "A valid password should be a mix of upper and lower case letters,\n" \
98 "digits and other characters. You can use a%s %d character long\n" \
99 "password with characters from at least 3 of these 4 classes, or\n" \
100 "a%s %d character long password containing characters from all the\n" \
101 "classes. Characters that form a common pattern are discarded by\n" \
103 #define MESSAGE_EXPLAIN_PASSPHRASE \
104 "A passphrase should be of at least %d words, %d to %d characters\n" \
105 "long and contain enough different characters.\n"
106 #define MESSAGE_RANDOM \
107 "Alternatively, if noone else can see your terminal now, you can\n" \
108 "pick this as your password: \"%s\".\n"
109 #define MESSAGE_RANDOMONLY \
110 "This system is configured to permit randomly generated passwords\n" \
111 "only. If noone else can see your terminal now, you can pick this\n" \
112 "as your password: \"%s\". Otherwise, come back later.\n"
113 #define MESSAGE_RANDOMFAILED \
114 "This system is configured to use randomly generated passwords\n" \
115 "only, but the attempt to generate a password has failed. This\n" \
116 "could happen for a number of reasons: you could have requested\n" \
117 "an impossible password length, or the access to kernel random\n" \
118 "number pool could have failed."
119 #define MESSAGE_TOOLONG \
120 "This password may be too long for some services. Choose another."
121 #define MESSAGE_TRUNCATED \
122 "Warning: your longer password will be truncated to 8 characters."
123 #define MESSAGE_WEAKPASS \
125 #define MESSAGE_NOTRANDOM \
126 "Sorry, you've mistyped the password that was generated for you."
127 #define MESSAGE_MISTYPED \
128 "Sorry, passwords do not match."
129 #define MESSAGE_RETRY \
132 static int converse(pam_handle_t
*pamh
, int style
, lo_const
char *text
,
133 struct pam_response
**resp
)
135 struct pam_conv
*conv
;
136 struct pam_message msg
, *pmsg
;
139 status
= pam_get_item(pamh
, PAM_CONV
, (pam_item_t
*)&conv
);
140 if (status
!= PAM_SUCCESS
)
144 msg
.msg_style
= style
;
148 return conv
->conv(1, (lo_const
struct pam_message
**)&pmsg
, resp
,
153 __attribute__ ((format (printf
, 3, 4)))
155 static int say(pam_handle_t
*pamh
, int style
, const char *format
, ...)
160 struct pam_response
*resp
;
163 va_start(args
, format
);
164 needed
= vsnprintf(buffer
, sizeof(buffer
), format
, args
);
167 if ((unsigned int)needed
< sizeof(buffer
)) {
168 status
= converse(pamh
, style
, buffer
, &resp
);
169 _pam_overwrite(buffer
);
172 memset(buffer
, 0, sizeof(buffer
));
178 static int check_max(params_t
*params
, pam_handle_t
*pamh
, const char *newpass
)
180 if ((int)strlen(newpass
) > params
->qc
.max
) {
181 if (params
->qc
.max
!= 8) {
182 say(pamh
, PAM_ERROR_MSG
, MESSAGE_TOOLONG
);
185 say(pamh
, PAM_TEXT_INFO
, MESSAGE_TRUNCATED
);
191 static int parse(params_t
*params
, pam_handle_t
*pamh
,
192 int argc
, const char **argv
)
200 if (!strncmp(*argv
, "min=", 4)) {
202 for (i
= 0; i
< 5; i
++) {
203 if (!strncmp(p
, "disabled", 8)) {
207 v
= strtoul(p
, &e
, 10);
210 if (i
< 4 && *p
++ != ',') break;
211 if (v
> INT_MAX
) break;
212 if (i
&& (int)v
> params
->qc
.min
[i
- 1]) break;
213 params
->qc
.min
[i
] = v
;
217 if (!strncmp(*argv
, "max=", 4)) {
218 v
= strtoul(*argv
+ 4, &e
, 10);
219 if (*e
|| v
< 8 || v
> INT_MAX
) break;
222 if (!strncmp(*argv
, "passphrase=", 11)) {
223 v
= strtoul(*argv
+ 11, &e
, 10);
224 if (*e
|| v
> INT_MAX
) break;
225 params
->qc
.passphrase_words
= v
;
227 if (!strncmp(*argv
, "match=", 6)) {
228 v
= strtoul(*argv
+ 6, &e
, 10);
229 if (*e
|| v
> INT_MAX
) break;
230 params
->qc
.match_length
= v
;
232 if (!strncmp(*argv
, "similar=", 8)) {
233 if (!strcmp(*argv
+ 8, "permit"))
234 params
->qc
.similar_deny
= 0;
236 if (!strcmp(*argv
+ 8, "deny"))
237 params
->qc
.similar_deny
= 1;
241 if (!strncmp(*argv
, "random=", 7)) {
242 v
= strtoul(*argv
+ 7, &e
, 10);
243 if (!strcmp(e
, ",only")) {
245 params
->qc
.min
[4] = INT_MAX
;
247 if (*e
|| v
> INT_MAX
) break;
248 params
->qc
.random_bits
= v
;
250 if (!strncmp(*argv
, "enforce=", 8)) {
251 params
->flags
&= ~F_ENFORCE_MASK
;
252 if (!strcmp(*argv
+ 8, "users"))
253 params
->flags
|= F_ENFORCE_USERS
;
255 if (!strcmp(*argv
+ 8, "everyone"))
256 params
->flags
|= F_ENFORCE_EVERYONE
;
258 if (strcmp(*argv
+ 8, "none"))
261 if (!strcmp(*argv
, "non-unix")) {
262 if (params
->flags
& F_CHECK_OLDAUTHTOK
) break;
263 params
->flags
|= F_NON_UNIX
;
265 if (!strncmp(*argv
, "retry=", 6)) {
266 v
= strtoul(*argv
+ 6, &e
, 10);
267 if (*e
|| v
> INT_MAX
) break;
270 if (!strncmp(*argv
, "ask_oldauthtok", 14)) {
271 params
->flags
&= ~F_ASK_OLDAUTHTOK_MASK
;
272 if (params
->flags
& F_USE_FIRST_PASS
) break;
273 if (!strcmp(*argv
+ 14, "=update"))
274 params
->flags
|= F_ASK_OLDAUTHTOK_UPDATE
;
277 params
->flags
|= F_ASK_OLDAUTHTOK_PRELIM
;
281 if (!strcmp(*argv
, "check_oldauthtok")) {
282 if (params
->flags
& F_NON_UNIX
) break;
283 params
->flags
|= F_CHECK_OLDAUTHTOK
;
285 if (!strcmp(*argv
, "use_first_pass")) {
286 if (params
->flags
& F_ASK_OLDAUTHTOK_MASK
) break;
287 params
->flags
|= F_USE_FIRST_PASS
| F_USE_AUTHTOK
;
289 if (!strcmp(*argv
, "use_authtok")) {
290 params
->flags
|= F_USE_AUTHTOK
;
297 say(pamh
, PAM_ERROR_MSG
, getuid() != 0 ?
298 MESSAGE_MISCONFIGURED
: MESSAGE_INVALID_OPTION
, *argv
);
305 PAM_EXTERN
int pam_sm_chauthtok(pam_handle_t
*pamh
, int flags
,
306 int argc
, const char **argv
)
309 struct pam_response
*resp
;
310 struct passwd
*pw
, fake_pw
;
314 char *user
, *oldpass
, *newpass
, *randompass
;
317 int randomonly
, enforce
, retries_left
, retry_wanted
;
321 status
= parse(¶ms
, pamh
, argc
, argv
);
322 if (status
!= PAM_SUCCESS
)
326 if (flags
& PAM_PRELIM_CHECK
) {
327 if (params
.flags
& F_ASK_OLDAUTHTOK_PRELIM
)
330 if (flags
& PAM_UPDATE_AUTHTOK
) {
331 if (params
.flags
& F_ASK_OLDAUTHTOK_UPDATE
)
334 return PAM_SERVICE_ERR
;
336 if (ask_oldauthtok
&& getuid() != 0) {
337 status
= converse(pamh
, PAM_PROMPT_ECHO_OFF
,
338 PROMPT_OLDPASS
, &resp
);
340 if (status
== PAM_SUCCESS
) {
341 if (resp
&& resp
->resp
) {
342 status
= pam_set_item(pamh
,
343 PAM_OLDAUTHTOK
, resp
->resp
);
344 _pam_drop_reply(resp
, 1);
346 status
= PAM_AUTHTOK_RECOVERY_ERR
;
349 if (status
!= PAM_SUCCESS
)
353 if (flags
& PAM_PRELIM_CHECK
)
356 status
= pam_get_item(pamh
, PAM_USER
, (pam_item_t
*)&user
);
357 if (status
!= PAM_SUCCESS
)
360 status
= pam_get_item(pamh
, PAM_OLDAUTHTOK
, (pam_item_t
*)&oldpass
);
361 if (status
!= PAM_SUCCESS
)
364 if (params
.flags
& F_NON_UNIX
) {
372 return PAM_USER_UNKNOWN
;
373 if ((params
.flags
& F_CHECK_OLDAUTHTOK
) && getuid() != 0) {
375 status
= PAM_AUTH_ERR
;
378 if (!strcmp(pw
->pw_passwd
, "x")) {
379 spw
= getspnam(user
);
382 if (strcmp(crypt(oldpass
, spw
->sp_pwdp
),
384 status
= PAM_AUTH_ERR
;
385 memset(spw
->sp_pwdp
, 0,
386 strlen(spw
->sp_pwdp
));
388 status
= PAM_AUTH_ERR
;
391 if (strcmp(crypt(oldpass
, pw
->pw_passwd
),
393 status
= PAM_AUTH_ERR
;
395 memset(pw
->pw_passwd
, 0, strlen(pw
->pw_passwd
));
396 if (status
!= PAM_SUCCESS
)
400 randomonly
= params
.qc
.min
[4] > params
.qc
.max
;
403 enforce
= params
.flags
& F_ENFORCE_USERS
;
405 enforce
= params
.flags
& F_ENFORCE_ROOT
;
407 if (params
.flags
& F_USE_AUTHTOK
) {
408 status
= pam_get_item(pamh
, PAM_AUTHTOK
,
409 (pam_item_t
*)&newpass
);
410 if (status
!= PAM_SUCCESS
)
412 if (!newpass
|| (check_max(¶ms
, pamh
, newpass
) && enforce
))
413 return PAM_AUTHTOK_ERR
;
414 reason
= _passwdqc_check(¶ms
.qc
, newpass
, oldpass
, pw
);
416 say(pamh
, PAM_ERROR_MSG
, MESSAGE_WEAKPASS
, reason
);
418 status
= PAM_AUTHTOK_ERR
;
423 retries_left
= params
.retry
;
429 params
.qc
.passphrase_words
&& params
.qc
.min
[2] <= params
.qc
.max
)
430 status
= say(pamh
, PAM_TEXT_INFO
, MESSAGE_INTRO_BOTH
);
432 status
= say(pamh
, PAM_TEXT_INFO
, MESSAGE_INTRO_PASSWORD
);
433 if (status
!= PAM_SUCCESS
)
436 if (!randomonly
&& params
.qc
.min
[3] <= params
.qc
.min
[4])
437 status
= say(pamh
, PAM_TEXT_INFO
, MESSAGE_EXPLAIN_PASSWORD_1
,
438 params
.qc
.min
[3] == 8 || params
.qc
.min
[3] == 11 ? "n" : "",
442 status
= say(pamh
, PAM_TEXT_INFO
, MESSAGE_EXPLAIN_PASSWORD_2
,
443 params
.qc
.min
[3] == 8 || params
.qc
.min
[3] == 11 ? "n" : "",
445 params
.qc
.min
[4] == 8 || params
.qc
.min
[4] == 11 ? "n" : "",
447 if (status
!= PAM_SUCCESS
)
451 params
.qc
.passphrase_words
&&
452 params
.qc
.min
[2] <= params
.qc
.max
) {
453 status
= say(pamh
, PAM_TEXT_INFO
, MESSAGE_EXPLAIN_PASSPHRASE
,
454 params
.qc
.passphrase_words
,
455 params
.qc
.min
[2], params
.qc
.max
);
456 if (status
!= PAM_SUCCESS
)
460 randompass
= _passwdqc_random(¶ms
.qc
);
462 status
= say(pamh
, PAM_TEXT_INFO
, randomonly
?
463 MESSAGE_RANDOMONLY
: MESSAGE_RANDOM
, randompass
);
464 if (status
!= PAM_SUCCESS
) {
465 _pam_overwrite(randompass
);
470 say(pamh
, PAM_ERROR_MSG
, getuid() != 0 ?
471 MESSAGE_MISCONFIGURED
: MESSAGE_RANDOMFAILED
);
472 return PAM_AUTHTOK_ERR
;
475 status
= converse(pamh
, PAM_PROMPT_ECHO_OFF
, PROMPT_NEWPASS1
, &resp
);
476 if (status
== PAM_SUCCESS
&& (!resp
|| !resp
->resp
))
477 status
= PAM_AUTHTOK_ERR
;
479 if (status
!= PAM_SUCCESS
) {
480 if (randompass
) _pam_overwrite(randompass
);
484 newpass
= strdup(resp
->resp
);
486 _pam_drop_reply(resp
, 1);
489 if (randompass
) _pam_overwrite(randompass
);
490 return PAM_AUTHTOK_ERR
;
493 if (check_max(¶ms
, pamh
, newpass
) && enforce
) {
494 status
= PAM_AUTHTOK_ERR
;
499 if (status
== PAM_SUCCESS
&&
500 (!randompass
|| !strstr(newpass
, randompass
)) &&
502 (reason
= _passwdqc_check(¶ms
.qc
, newpass
, oldpass
, pw
)))) {
504 say(pamh
, PAM_ERROR_MSG
, MESSAGE_NOTRANDOM
);
506 say(pamh
, PAM_ERROR_MSG
, MESSAGE_WEAKPASS
, reason
);
508 status
= PAM_AUTHTOK_ERR
;
513 if (status
== PAM_SUCCESS
)
514 status
= converse(pamh
, PAM_PROMPT_ECHO_OFF
,
515 PROMPT_NEWPASS2
, &resp
);
516 if (status
== PAM_SUCCESS
) {
517 if (resp
&& resp
->resp
) {
518 if (strcmp(newpass
, resp
->resp
)) {
520 PAM_ERROR_MSG
, MESSAGE_MISTYPED
);
521 if (status
== PAM_SUCCESS
) {
522 status
= PAM_AUTHTOK_ERR
;
526 _pam_drop_reply(resp
, 1);
528 status
= PAM_AUTHTOK_ERR
;
531 if (status
== PAM_SUCCESS
)
532 status
= pam_set_item(pamh
, PAM_AUTHTOK
, newpass
);
534 if (randompass
) _pam_overwrite(randompass
);
535 _pam_overwrite(newpass
);
538 if (retry_wanted
&& --retries_left
> 0) {
539 status
= say(pamh
, PAM_TEXT_INFO
, MESSAGE_RETRY
);
540 if (status
== PAM_SUCCESS
)
547 #ifdef PAM_MODULE_ENTRY
548 PAM_MODULE_ENTRY("pam_passwdqc");
549 #elif defined(PAM_STATIC)
550 struct pam_module _pam_passwdqc_modstruct
= {