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