Ignore machine-check MSRs
[freebsd-src/fkvm-freebsd.git] / contrib / pam_modules / pam_passwdqc / pam_passwdqc.c
blob7c3973105cd293395a188470501b6b8dd448e07c
1 /*
2 * Copyright (c) 2000-2002 by Solar Designer. See LICENSE.
3 */
5 #define _XOPEN_SOURCE 500
6 #define _XOPEN_SOURCE_EXTENDED
7 #define _XOPEN_VERSION 500
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #include <string.h>
12 #include <limits.h>
13 #include <unistd.h>
14 #include <pwd.h>
15 #ifdef HAVE_SHADOW
16 #include <shadow.h>
17 #endif
19 #define PAM_SM_PASSWORD
20 #ifndef LINUX_PAM
21 #include <security/pam_appl.h>
22 #endif
23 #include <security/pam_modules.h>
25 #include "pam_macros.h"
27 #if !defined(PAM_EXTERN) && !defined(PAM_STATIC)
28 #define PAM_EXTERN extern
29 #endif
31 #if !defined(PAM_AUTHTOK_RECOVERY_ERR) && defined(PAM_AUTHTOK_RECOVER_ERR)
32 #define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR
33 #endif
35 #if defined(__sun__) && !defined(LINUX_PAM) && !defined(_OPENPAM)
36 /* Sun's PAM doesn't use const here */
37 #define lo_const
38 #else
39 #define lo_const const
40 #endif
41 typedef lo_const void *pam_item_t;
43 #include "passwdqc.h"
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
57 typedef struct {
58 passwdqc_params_t qc;
59 int flags;
60 int retry;
61 } params_t;
63 static params_t defaults = {
65 {INT_MAX, 24, 12, 8, 7}, /* min */
66 40, /* max */
67 3, /* passphrase_words */
68 4, /* match_length */
69 1, /* similar_deny */
70 42 /* random_bits */
72 F_ENFORCE_EVERYONE, /* flags */
73 3 /* retry */
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" \
102 "the check.\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 \
124 "Weak password: %s."
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 \
130 "Try again."
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;
137 int status;
139 status = pam_get_item(pamh, PAM_CONV, (pam_item_t *)&conv);
140 if (status != PAM_SUCCESS)
141 return status;
143 pmsg = &msg;
144 msg.msg_style = style;
145 msg.msg = text;
147 *resp = NULL;
148 return conv->conv(1, (lo_const struct pam_message **)&pmsg, resp,
149 conv->appdata_ptr);
152 #ifdef __GNUC__
153 __attribute__ ((format (printf, 3, 4)))
154 #endif
155 static int say(pam_handle_t *pamh, int style, const char *format, ...)
157 va_list args;
158 char buffer[0x800];
159 int needed;
160 struct pam_response *resp;
161 int status;
163 va_start(args, format);
164 needed = vsnprintf(buffer, sizeof(buffer), format, args);
165 va_end(args);
167 if ((unsigned int)needed < sizeof(buffer)) {
168 status = converse(pamh, style, buffer, &resp);
169 _pam_overwrite(buffer);
170 } else {
171 status = PAM_ABORT;
172 memset(buffer, 0, sizeof(buffer));
175 return status;
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);
183 return -1;
185 say(pamh, PAM_TEXT_INFO, MESSAGE_TRUNCATED);
188 return 0;
191 static int parse(params_t *params, pam_handle_t *pamh,
192 int argc, const char **argv)
194 const char *p;
195 char *e;
196 int i;
197 unsigned long v;
199 while (argc) {
200 if (!strncmp(*argv, "min=", 4)) {
201 p = *argv + 4;
202 for (i = 0; i < 5; i++) {
203 if (!strncmp(p, "disabled", 8)) {
204 v = INT_MAX;
205 p += 8;
206 } else {
207 v = strtoul(p, &e, 10);
208 p = e;
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;
215 if (*p) break;
216 } else
217 if (!strncmp(*argv, "max=", 4)) {
218 v = strtoul(*argv + 4, &e, 10);
219 if (*e || v < 8 || v > INT_MAX) break;
220 params->qc.max = v;
221 } else
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;
226 } else
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;
231 } else
232 if (!strncmp(*argv, "similar=", 8)) {
233 if (!strcmp(*argv + 8, "permit"))
234 params->qc.similar_deny = 0;
235 else
236 if (!strcmp(*argv + 8, "deny"))
237 params->qc.similar_deny = 1;
238 else
239 break;
240 } else
241 if (!strncmp(*argv, "random=", 7)) {
242 v = strtoul(*argv + 7, &e, 10);
243 if (!strcmp(e, ",only")) {
244 e += 5;
245 params->qc.min[4] = INT_MAX;
247 if (*e || v > INT_MAX) break;
248 params->qc.random_bits = v;
249 } else
250 if (!strncmp(*argv, "enforce=", 8)) {
251 params->flags &= ~F_ENFORCE_MASK;
252 if (!strcmp(*argv + 8, "users"))
253 params->flags |= F_ENFORCE_USERS;
254 else
255 if (!strcmp(*argv + 8, "everyone"))
256 params->flags |= F_ENFORCE_EVERYONE;
257 else
258 if (strcmp(*argv + 8, "none"))
259 break;
260 } else
261 if (!strcmp(*argv, "non-unix")) {
262 if (params->flags & F_CHECK_OLDAUTHTOK) break;
263 params->flags |= F_NON_UNIX;
264 } else
265 if (!strncmp(*argv, "retry=", 6)) {
266 v = strtoul(*argv + 6, &e, 10);
267 if (*e || v > INT_MAX) break;
268 params->retry = v;
269 } else
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;
275 else
276 if (!(*argv)[14])
277 params->flags |= F_ASK_OLDAUTHTOK_PRELIM;
278 else
279 break;
280 } else
281 if (!strcmp(*argv, "check_oldauthtok")) {
282 if (params->flags & F_NON_UNIX) break;
283 params->flags |= F_CHECK_OLDAUTHTOK;
284 } else
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;
288 } else
289 if (!strcmp(*argv, "use_authtok")) {
290 params->flags |= F_USE_AUTHTOK;
291 } else
292 break;
293 argc--; argv++;
296 if (argc) {
297 say(pamh, PAM_ERROR_MSG, getuid() != 0 ?
298 MESSAGE_MISCONFIGURED : MESSAGE_INVALID_OPTION, *argv);
299 return PAM_ABORT;
302 return PAM_SUCCESS;
305 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
306 int argc, const char **argv)
308 params_t params;
309 struct pam_response *resp;
310 struct passwd *pw, fake_pw;
311 #ifdef HAVE_SHADOW
312 struct spwd *spw;
313 #endif
314 char *user, *oldpass, *newpass, *randompass;
315 const char *reason;
316 int ask_oldauthtok;
317 int randomonly, enforce, retries_left, retry_wanted;
318 int status;
320 params = defaults;
321 status = parse(&params, pamh, argc, argv);
322 if (status != PAM_SUCCESS)
323 return status;
325 ask_oldauthtok = 0;
326 if (flags & PAM_PRELIM_CHECK) {
327 if (params.flags & F_ASK_OLDAUTHTOK_PRELIM)
328 ask_oldauthtok = 1;
329 } else
330 if (flags & PAM_UPDATE_AUTHTOK) {
331 if (params.flags & F_ASK_OLDAUTHTOK_UPDATE)
332 ask_oldauthtok = 1;
333 } else
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);
345 } else
346 status = PAM_AUTHTOK_RECOVERY_ERR;
349 if (status != PAM_SUCCESS)
350 return status;
353 if (flags & PAM_PRELIM_CHECK)
354 return status;
356 status = pam_get_item(pamh, PAM_USER, (pam_item_t *)&user);
357 if (status != PAM_SUCCESS)
358 return status;
360 status = pam_get_item(pamh, PAM_OLDAUTHTOK, (pam_item_t *)&oldpass);
361 if (status != PAM_SUCCESS)
362 return status;
364 if (params.flags & F_NON_UNIX) {
365 pw = &fake_pw;
366 pw->pw_name = user;
367 pw->pw_gecos = "";
368 } else {
369 pw = getpwnam(user);
370 endpwent();
371 if (!pw)
372 return PAM_USER_UNKNOWN;
373 if ((params.flags & F_CHECK_OLDAUTHTOK) && getuid() != 0) {
374 if (!oldpass)
375 status = PAM_AUTH_ERR;
376 else
377 #ifdef HAVE_SHADOW
378 if (!strcmp(pw->pw_passwd, "x")) {
379 spw = getspnam(user);
380 endspent();
381 if (spw) {
382 if (strcmp(crypt(oldpass, spw->sp_pwdp),
383 spw->sp_pwdp))
384 status = PAM_AUTH_ERR;
385 memset(spw->sp_pwdp, 0,
386 strlen(spw->sp_pwdp));
387 } else
388 status = PAM_AUTH_ERR;
389 } else
390 #endif
391 if (strcmp(crypt(oldpass, pw->pw_passwd),
392 pw->pw_passwd))
393 status = PAM_AUTH_ERR;
395 memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
396 if (status != PAM_SUCCESS)
397 return status;
400 randomonly = params.qc.min[4] > params.qc.max;
402 if (getuid() != 0)
403 enforce = params.flags & F_ENFORCE_USERS;
404 else
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)
411 return status;
412 if (!newpass || (check_max(&params, pamh, newpass) && enforce))
413 return PAM_AUTHTOK_ERR;
414 reason = _passwdqc_check(&params.qc, newpass, oldpass, pw);
415 if (reason) {
416 say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason);
417 if (enforce)
418 status = PAM_AUTHTOK_ERR;
420 return status;
423 retries_left = params.retry;
425 retry:
426 retry_wanted = 0;
428 if (!randomonly &&
429 params.qc.passphrase_words && params.qc.min[2] <= params.qc.max)
430 status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_BOTH);
431 else
432 status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_PASSWORD);
433 if (status != PAM_SUCCESS)
434 return status;
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" : "",
439 params.qc.min[3]);
440 else
441 if (!randomonly)
442 status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_2,
443 params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "",
444 params.qc.min[3],
445 params.qc.min[4] == 8 || params.qc.min[4] == 11 ? "n" : "",
446 params.qc.min[4]);
447 if (status != PAM_SUCCESS)
448 return status;
450 if (!randomonly &&
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)
457 return status;
460 randompass = _passwdqc_random(&params.qc);
461 if (randompass) {
462 status = say(pamh, PAM_TEXT_INFO, randomonly ?
463 MESSAGE_RANDOMONLY : MESSAGE_RANDOM, randompass);
464 if (status != PAM_SUCCESS) {
465 _pam_overwrite(randompass);
466 randompass = NULL;
468 } else
469 if (randomonly) {
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);
481 return status;
484 newpass = strdup(resp->resp);
486 _pam_drop_reply(resp, 1);
488 if (!newpass) {
489 if (randompass) _pam_overwrite(randompass);
490 return PAM_AUTHTOK_ERR;
493 if (check_max(&params, pamh, newpass) && enforce) {
494 status = PAM_AUTHTOK_ERR;
495 retry_wanted = 1;
498 reason = NULL;
499 if (status == PAM_SUCCESS &&
500 (!randompass || !strstr(newpass, randompass)) &&
501 (randomonly ||
502 (reason = _passwdqc_check(&params.qc, newpass, oldpass, pw)))) {
503 if (randomonly)
504 say(pamh, PAM_ERROR_MSG, MESSAGE_NOTRANDOM);
505 else
506 say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason);
507 if (enforce) {
508 status = PAM_AUTHTOK_ERR;
509 retry_wanted = 1;
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)) {
519 status = say(pamh,
520 PAM_ERROR_MSG, MESSAGE_MISTYPED);
521 if (status == PAM_SUCCESS) {
522 status = PAM_AUTHTOK_ERR;
523 retry_wanted = 1;
526 _pam_drop_reply(resp, 1);
527 } else
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);
536 free(newpass);
538 if (retry_wanted && --retries_left > 0) {
539 status = say(pamh, PAM_TEXT_INFO, MESSAGE_RETRY);
540 if (status == PAM_SUCCESS)
541 goto retry;
544 return status;
547 #ifdef PAM_MODULE_ENTRY
548 PAM_MODULE_ENTRY("pam_passwdqc");
549 #elif defined(PAM_STATIC)
550 struct pam_module _pam_passwdqc_modstruct = {
551 "pam_passwdqc",
552 NULL,
553 NULL,
554 NULL,
555 NULL,
556 NULL,
557 pam_sm_chauthtok
559 #endif