1*8c117293SSascha Wildner /*
2*8c117293SSascha Wildner * Copyright (c) 2000-2003,2005,2016,2020,2021 by Solar Designer
3*8c117293SSascha Wildner * Copyright (c) 2008,2009 by Dmitry V. Levin
4*8c117293SSascha Wildner * See LICENSE
5*8c117293SSascha Wildner */
6*8c117293SSascha Wildner
7*8c117293SSascha Wildner #ifdef _MSC_VER
8*8c117293SSascha Wildner #define _CRT_NONSTDC_NO_WARNINGS /* we use POSIX function names */
9*8c117293SSascha Wildner #endif
10*8c117293SSascha Wildner
11*8c117293SSascha Wildner #include <stdio.h>
12*8c117293SSascha Wildner #include <stdlib.h>
13*8c117293SSascha Wildner #include <string.h>
14*8c117293SSascha Wildner #include <limits.h>
15*8c117293SSascha Wildner
16*8c117293SSascha Wildner #include "passwdqc.h"
17*8c117293SSascha Wildner #include "concat.h"
18*8c117293SSascha Wildner
skip_prefix(const char * sample,const char * prefix)19*8c117293SSascha Wildner static const char *skip_prefix(const char *sample, const char *prefix)
20*8c117293SSascha Wildner {
21*8c117293SSascha Wildner size_t len = strlen(prefix);
22*8c117293SSascha Wildner
23*8c117293SSascha Wildner if (strncmp(sample, prefix, len))
24*8c117293SSascha Wildner return NULL;
25*8c117293SSascha Wildner return sample + len;
26*8c117293SSascha Wildner }
27*8c117293SSascha Wildner
28*8c117293SSascha Wildner static int
parse_option(passwdqc_params_t * params,char ** reason,const char * option)29*8c117293SSascha Wildner parse_option(passwdqc_params_t *params, char **reason, const char *option)
30*8c117293SSascha Wildner {
31*8c117293SSascha Wildner const char *err = "Invalid parameter value";
32*8c117293SSascha Wildner const char * const err_oom = "Out of memory";
33*8c117293SSascha Wildner const char *p;
34*8c117293SSascha Wildner char *e;
35*8c117293SSascha Wildner int i, rc = 0;
36*8c117293SSascha Wildner unsigned long v;
37*8c117293SSascha Wildner
38*8c117293SSascha Wildner *reason = NULL;
39*8c117293SSascha Wildner if ((p = skip_prefix(option, "min="))) {
40*8c117293SSascha Wildner for (i = 0; i < 5; i++) {
41*8c117293SSascha Wildner if (!strncmp(p, "disabled", 8)) {
42*8c117293SSascha Wildner v = INT_MAX;
43*8c117293SSascha Wildner p += 8;
44*8c117293SSascha Wildner } else {
45*8c117293SSascha Wildner v = strtoul(p, &e, 10);
46*8c117293SSascha Wildner p = e;
47*8c117293SSascha Wildner }
48*8c117293SSascha Wildner if (i < 4 && *p++ != ',')
49*8c117293SSascha Wildner goto parse_error;
50*8c117293SSascha Wildner if (v > INT_MAX)
51*8c117293SSascha Wildner goto parse_error;
52*8c117293SSascha Wildner if (i && (int)v > params->qc.min[i - 1])
53*8c117293SSascha Wildner goto parse_error;
54*8c117293SSascha Wildner params->qc.min[i] = v;
55*8c117293SSascha Wildner }
56*8c117293SSascha Wildner if (*p)
57*8c117293SSascha Wildner goto parse_error;
58*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "max="))) {
59*8c117293SSascha Wildner v = strtoul(p, &e, 10);
60*8c117293SSascha Wildner if (*e || v < 8 || v > INT_MAX)
61*8c117293SSascha Wildner goto parse_error;
62*8c117293SSascha Wildner if (v > 10000)
63*8c117293SSascha Wildner v = 10000;
64*8c117293SSascha Wildner params->qc.max = v;
65*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "passphrase="))) {
66*8c117293SSascha Wildner v = strtoul(p, &e, 10);
67*8c117293SSascha Wildner if (*e || v > INT_MAX)
68*8c117293SSascha Wildner goto parse_error;
69*8c117293SSascha Wildner params->qc.passphrase_words = v;
70*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "match="))) {
71*8c117293SSascha Wildner v = strtoul(p, &e, 10);
72*8c117293SSascha Wildner if (*e || v > INT_MAX)
73*8c117293SSascha Wildner goto parse_error;
74*8c117293SSascha Wildner params->qc.match_length = v;
75*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "similar="))) {
76*8c117293SSascha Wildner if (!strcmp(p, "permit"))
77*8c117293SSascha Wildner params->qc.similar_deny = 0;
78*8c117293SSascha Wildner else if (!strcmp(p, "deny"))
79*8c117293SSascha Wildner params->qc.similar_deny = 1;
80*8c117293SSascha Wildner else
81*8c117293SSascha Wildner goto parse_error;
82*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "random="))) {
83*8c117293SSascha Wildner v = strtoul(p, &e, 10);
84*8c117293SSascha Wildner if (!strcmp(e, ",only")) {
85*8c117293SSascha Wildner e += 5;
86*8c117293SSascha Wildner params->qc.min[4] = INT_MAX;
87*8c117293SSascha Wildner }
88*8c117293SSascha Wildner if (*e || (v && v < 24) || v > 136)
89*8c117293SSascha Wildner goto parse_error;
90*8c117293SSascha Wildner params->qc.random_bits = v;
91*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "wordlist="))) {
92*8c117293SSascha Wildner free(params->qc.wordlist);
93*8c117293SSascha Wildner params->qc.wordlist = NULL;
94*8c117293SSascha Wildner if (*p && !(params->qc.wordlist = strdup(p))) {
95*8c117293SSascha Wildner err = err_oom;
96*8c117293SSascha Wildner goto parse_error;
97*8c117293SSascha Wildner }
98*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "denylist="))) {
99*8c117293SSascha Wildner free(params->qc.denylist);
100*8c117293SSascha Wildner params->qc.denylist = NULL;
101*8c117293SSascha Wildner if (*p && !(params->qc.denylist = strdup(p))) {
102*8c117293SSascha Wildner err = err_oom;
103*8c117293SSascha Wildner goto parse_error;
104*8c117293SSascha Wildner }
105*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "filter="))) {
106*8c117293SSascha Wildner free(params->qc.filter);
107*8c117293SSascha Wildner params->qc.filter = NULL;
108*8c117293SSascha Wildner if (*p && !(params->qc.filter = strdup(p))) {
109*8c117293SSascha Wildner err = err_oom;
110*8c117293SSascha Wildner goto parse_error;
111*8c117293SSascha Wildner }
112*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "enforce="))) {
113*8c117293SSascha Wildner params->pam.flags &= ~F_ENFORCE_MASK;
114*8c117293SSascha Wildner if (!strcmp(p, "users"))
115*8c117293SSascha Wildner params->pam.flags |= F_ENFORCE_USERS;
116*8c117293SSascha Wildner else if (!strcmp(p, "everyone"))
117*8c117293SSascha Wildner params->pam.flags |= F_ENFORCE_EVERYONE;
118*8c117293SSascha Wildner else if (strcmp(p, "none"))
119*8c117293SSascha Wildner goto parse_error;
120*8c117293SSascha Wildner } else if (!strcmp(option, "non-unix")) {
121*8c117293SSascha Wildner if (params->pam.flags & F_CHECK_OLDAUTHTOK)
122*8c117293SSascha Wildner goto parse_error;
123*8c117293SSascha Wildner params->pam.flags |= F_NON_UNIX;
124*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "retry="))) {
125*8c117293SSascha Wildner v = strtoul(p, &e, 10);
126*8c117293SSascha Wildner if (*e || v > INT_MAX)
127*8c117293SSascha Wildner goto parse_error;
128*8c117293SSascha Wildner params->pam.retry = v;
129*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "ask_oldauthtok"))) {
130*8c117293SSascha Wildner params->pam.flags &= ~F_ASK_OLDAUTHTOK_MASK;
131*8c117293SSascha Wildner if (params->pam.flags & F_USE_FIRST_PASS)
132*8c117293SSascha Wildner goto parse_error;
133*8c117293SSascha Wildner if (!p[0])
134*8c117293SSascha Wildner params->pam.flags |= F_ASK_OLDAUTHTOK_PRELIM;
135*8c117293SSascha Wildner else if (!strcmp(p, "=update"))
136*8c117293SSascha Wildner params->pam.flags |= F_ASK_OLDAUTHTOK_UPDATE;
137*8c117293SSascha Wildner else
138*8c117293SSascha Wildner goto parse_error;
139*8c117293SSascha Wildner } else if (!strcmp(option, "check_oldauthtok")) {
140*8c117293SSascha Wildner if (params->pam.flags & F_NON_UNIX)
141*8c117293SSascha Wildner goto parse_error;
142*8c117293SSascha Wildner params->pam.flags |= F_CHECK_OLDAUTHTOK;
143*8c117293SSascha Wildner } else if (!strcmp(option, "use_first_pass")) {
144*8c117293SSascha Wildner if (params->pam.flags & F_ASK_OLDAUTHTOK_MASK)
145*8c117293SSascha Wildner goto parse_error;
146*8c117293SSascha Wildner params->pam.flags |= F_USE_FIRST_PASS | F_USE_AUTHTOK;
147*8c117293SSascha Wildner } else if (!strcmp(option, "use_authtok")) {
148*8c117293SSascha Wildner params->pam.flags |= F_USE_AUTHTOK;
149*8c117293SSascha Wildner } else if (!strcmp(option, "noaudit")) {
150*8c117293SSascha Wildner params->pam.flags |= F_NO_AUDIT;
151*8c117293SSascha Wildner } else if ((p = skip_prefix(option, "config="))) {
152*8c117293SSascha Wildner if ((rc = passwdqc_params_load(params, reason, p)))
153*8c117293SSascha Wildner goto parse_error;
154*8c117293SSascha Wildner } else {
155*8c117293SSascha Wildner err = "Invalid parameter";
156*8c117293SSascha Wildner goto parse_error;
157*8c117293SSascha Wildner }
158*8c117293SSascha Wildner
159*8c117293SSascha Wildner return 0;
160*8c117293SSascha Wildner
161*8c117293SSascha Wildner parse_error:
162*8c117293SSascha Wildner passwdqc_params_free(params);
163*8c117293SSascha Wildner e = concat("Error parsing parameter \"", option, "\": ",
164*8c117293SSascha Wildner (rc ? (*reason ? *reason : err_oom) : err), NULL);
165*8c117293SSascha Wildner free(*reason);
166*8c117293SSascha Wildner *reason = e;
167*8c117293SSascha Wildner return rc ? rc : -1;
168*8c117293SSascha Wildner }
169*8c117293SSascha Wildner
170*8c117293SSascha Wildner int
passwdqc_params_parse(passwdqc_params_t * params,char ** reason,int argc,const char * const * argv)171*8c117293SSascha Wildner passwdqc_params_parse(passwdqc_params_t *params, char **reason,
172*8c117293SSascha Wildner int argc, const char *const *argv)
173*8c117293SSascha Wildner {
174*8c117293SSascha Wildner int i;
175*8c117293SSascha Wildner
176*8c117293SSascha Wildner *reason = NULL;
177*8c117293SSascha Wildner for (i = 0; i < argc; ++i) {
178*8c117293SSascha Wildner int rc;
179*8c117293SSascha Wildner
180*8c117293SSascha Wildner if ((rc = parse_option(params, reason, argv[i])))
181*8c117293SSascha Wildner return rc;
182*8c117293SSascha Wildner }
183*8c117293SSascha Wildner return 0;
184*8c117293SSascha Wildner }
185*8c117293SSascha Wildner
186*8c117293SSascha Wildner static const passwdqc_params_t defaults = {
187*8c117293SSascha Wildner {
188*8c117293SSascha Wildner {INT_MAX, 24, 11, 8, 7}, /* min */
189*8c117293SSascha Wildner 72, /* max */
190*8c117293SSascha Wildner 3, /* passphrase_words */
191*8c117293SSascha Wildner 4, /* match_length */
192*8c117293SSascha Wildner 1, /* similar_deny */
193*8c117293SSascha Wildner 47, /* random_bits */
194*8c117293SSascha Wildner NULL, /* wordlist */
195*8c117293SSascha Wildner NULL, /* denylist */
196*8c117293SSascha Wildner NULL /* filter */
197*8c117293SSascha Wildner },
198*8c117293SSascha Wildner {
199*8c117293SSascha Wildner F_ENFORCE_EVERYONE, /* flags */
200*8c117293SSascha Wildner 3 /* retry */
201*8c117293SSascha Wildner }
202*8c117293SSascha Wildner };
203*8c117293SSascha Wildner
passwdqc_params_reset(passwdqc_params_t * params)204*8c117293SSascha Wildner void passwdqc_params_reset(passwdqc_params_t *params)
205*8c117293SSascha Wildner {
206*8c117293SSascha Wildner *params = defaults;
207*8c117293SSascha Wildner }
208*8c117293SSascha Wildner
passwdqc_params_free(passwdqc_params_t * params)209*8c117293SSascha Wildner void passwdqc_params_free(passwdqc_params_t *params)
210*8c117293SSascha Wildner {
211*8c117293SSascha Wildner free(params->qc.wordlist);
212*8c117293SSascha Wildner free(params->qc.denylist);
213*8c117293SSascha Wildner free(params->qc.filter);
214*8c117293SSascha Wildner passwdqc_params_reset(params);
215*8c117293SSascha Wildner }
216