Fix mdoc(7)/man(7) mix up.
[netbsd-mini2440.git] / lib / libutil / pw_policy.c
blobb0c0539744bfc196724b53b6bacf3c470b8d312c
1 /* $NetBSD: pw_policy.c,v 1.13 2007/01/09 14:04:44 elad Exp $ */
3 /*-
4 * Copyright (c) 2005, 2006 Elad Efrat <elad@NetBSD.org>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <stdio.h>
31 #include <util.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <limits.h>
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <pwd.h>
40 #include <grp.h>
41 #include <assert.h>
43 #include <machine/int_limits.h>
45 #define PW_POLICY_DEFAULTKEY "pw_policy"
47 #define LOAD_POLICY 0
48 #define TEST_POLICY 1
50 #define MINMAX_ERR(min, max, n) ((min == 0 || max == 0) && n != 0) || \
51 (min > 0 && min > n) || \
52 (max > 0 && max < n)
54 #define HANDLER_PROTO pw_policy_t, int, char *, void *, void *
55 #define HANDLER_ARGS pw_policy_t policy, int flag, char *pw, void *arg, void *arg2
57 static int pw_policy_parse_num(char *, int32_t *);
58 static int pw_policy_parse_range(char *, int32_t *, int32_t *);
60 static int pw_policy_handle_len(HANDLER_PROTO);
61 static int pw_policy_handle_charclass(HANDLER_PROTO);
62 static int pw_policy_handle_nclasses(HANDLER_PROTO);
63 static int pw_policy_handle_ntoggles(HANDLER_PROTO);
65 struct pw_policy {
66 int32_t minlen;
67 int32_t maxlen;
68 int32_t minupper;
69 int32_t maxupper;
70 int32_t minlower;
71 int32_t maxlower;
72 int32_t mindigits;
73 int32_t maxdigits;
74 int32_t minpunct;
75 int32_t maxpunct;
76 int32_t mintoggles;
77 int32_t maxtoggles;
78 int32_t minclasses;
79 int32_t maxclasses;
82 struct pw_policy_handler {
83 const char *name;
84 int (*handler)(HANDLER_PROTO);
85 void *arg2;
88 static struct pw_policy_handler handlers[] = {
89 { "length", pw_policy_handle_len, NULL },
90 { "lowercase", pw_policy_handle_charclass, islower },
91 { "uppercase", pw_policy_handle_charclass, isupper },
92 { "digits", pw_policy_handle_charclass, isdigit },
93 { "punctuation", pw_policy_handle_charclass, ispunct },
94 { "nclasses", pw_policy_handle_nclasses, NULL },
95 { "ntoggles", pw_policy_handle_ntoggles, NULL },
96 { NULL, NULL, NULL },
99 static int
100 pw_policy_parse_num(char *s, int32_t *n)
102 char *endptr = NULL;
103 long l;
105 if (s[0] == '*' && s[1] == '\0') {
106 *n = -1;
107 return 0;
110 errno = 0;
111 l = strtol(s, &endptr, 10);
112 if (*endptr != '\0')
113 return EINVAL;
114 if (errno == ERANGE && (l == LONG_MIN || l == LONG_MAX))
115 return ERANGE;
117 if (l < 0 || l >= UINT16_MAX)
118 return ERANGE;
120 *n = (int32_t)l;
122 return 0;
125 static int
126 pw_policy_parse_range(char *range, int32_t *n, int32_t *m)
128 char *p;
130 while (isspace((unsigned char)*range))
131 range++;
132 p = &range[strlen(range) - 1];
133 while (isspace((unsigned char)*p))
134 *p-- = '\0';
136 /* Single characters: * = any number, 0 = none. */
137 if (range[0] == '0' && range[1] == '\0') {
138 *n = *m = 0;
139 return 0;
141 if (range[0] == '*' && range[1] == '\0') {
142 *n = *m = -1;
143 return 0;
146 /* Get range, N-M. */
147 p = strchr(range, '-');
148 if (p == NULL) {
149 /* Exact match. */
150 if (pw_policy_parse_num(range, n) != 0)
151 return EINVAL;
153 *m = *n;
155 return 0;
157 *p++ = '\0';
159 if (pw_policy_parse_num(range, n) != 0 ||
160 pw_policy_parse_num(p, m) != 0)
161 return EINVAL;
163 return (0);
166 /*ARGSUSED*/
167 static int
168 pw_policy_handle_len(HANDLER_ARGS __unused)
170 size_t len;
172 switch (flag) {
173 case LOAD_POLICY:
174 /* Here, '0' and '*' mean the same. */
175 if (pw_policy_parse_range(arg, &policy->minlen,
176 &policy->maxlen) != 0)
177 return EINVAL;
179 return 0;
181 case TEST_POLICY:
182 len = strlen(pw);
184 if ((policy->minlen > 0 && (int32_t)len < policy->minlen) ||
185 (policy->maxlen > 0 && (int32_t)len > policy->maxlen))
186 return EPERM;
187 return 0;
188 default:
190 return EINVAL;
194 static int
195 pw_policy_handle_charclass(HANDLER_ARGS)
197 int (*ischarclass)(int);
198 int32_t *n = NULL, *m = NULL, count = 0;
200 if (arg2 == islower) {
201 n = &policy->minlower;
202 m = &policy->maxlower;
203 } else if (arg2 == isupper) {
204 n = &policy->minupper;
205 m = &policy->maxupper;
206 } else if (arg2 == isdigit) {
207 n = &policy->mindigits;
208 m = &policy->maxdigits;
209 } else if (arg2 == ispunct) {
210 n = &policy->minpunct;
211 m = &policy->maxpunct;
212 } else
213 return EINVAL;
215 switch (flag) {
216 case LOAD_POLICY:
217 if ((pw_policy_parse_range(arg, n, m) != 0))
218 return EINVAL;
219 return 0;
221 case TEST_POLICY:
222 ischarclass = arg2;
224 do {
225 if (ischarclass((unsigned char)*pw++))
226 count++;
227 } while (*pw != '\0');
229 if (MINMAX_ERR(*n, *m, count))
230 return EPERM;
231 return 0;
233 default:
234 return EINVAL;
238 /*ARGSUSED*/
239 static int
240 pw_policy_handle_nclasses(HANDLER_ARGS __unused)
242 switch (flag) {
243 case LOAD_POLICY:
244 if (pw_policy_parse_range(arg, &policy->minclasses,
245 &policy->maxclasses) != 0)
246 return EINVAL;
248 return 0;
250 case TEST_POLICY: {
251 int have_lower = 0, have_upper = 0, have_digit = 0,
252 have_punct = 0;
253 int32_t nsets = 0;
255 while (*pw != '\0') {
256 if (islower((unsigned char)*pw) && !have_lower) {
257 have_lower = 1;
258 nsets++;
259 } else if (isupper((unsigned char)*pw) && !have_upper) {
260 have_upper = 1;
261 nsets++;
262 } else if (isdigit((unsigned char)*pw) && !have_digit) {
263 have_digit = 1;
264 nsets++;
265 } else if (ispunct((unsigned char)*pw) && !have_punct) {
266 have_punct = 1;
267 nsets++;
269 pw++;
272 if (MINMAX_ERR(policy->minclasses, policy->maxclasses, nsets))
273 return EPERM;
274 return 0;
276 default:
277 return EINVAL;
281 /*ARGSUSED*/
282 static int
283 pw_policy_handle_ntoggles(HANDLER_ARGS __unused)
285 switch (flag) {
286 case LOAD_POLICY:
287 if (pw_policy_parse_range(arg, &policy->mintoggles,
288 &policy->maxtoggles) != 0)
289 return EINVAL;
290 return 0;
292 case TEST_POLICY: {
293 int previous_set = 0, current_set = 0;
294 int32_t ntoggles = 0, current_ntoggles = 0;
296 #define CHAR_CLASS(c) (islower((unsigned char)(c)) ? 1 : \
297 isupper((unsigned char)(c)) ? 2 : \
298 isdigit((unsigned char)(c)) ? 3 : \
299 ispunct((unsigned char)(c)) ? 4 : \
302 while (*pw != '\0') {
303 if (!isspace((unsigned char)*pw)) {
304 current_set = CHAR_CLASS(*pw);
305 if (!current_set) {
306 return EINVAL;
309 if (!previous_set ||
310 current_set == previous_set) {
311 current_ntoggles++;
312 } else {
313 if (current_ntoggles > ntoggles)
314 ntoggles = current_ntoggles;
316 current_ntoggles = 1;
319 previous_set = current_set;
322 pw++;
325 #undef CHAR_CLASS
327 if (MINMAX_ERR(policy->mintoggles, policy->maxtoggles,
328 ntoggles))
329 return EPERM;
331 return 0;
333 default:
334 return EINVAL;
338 pw_policy_t
339 pw_policy_load(void *key, int how)
341 pw_policy_t policy;
342 struct pw_policy_handler *hp;
343 char buf[BUFSIZ];
345 /* If there's no /etc/passwd.conf, don't touch the policy. */
346 if (access(_PATH_PASSWD_CONF, R_OK) == -1)
347 return NULL;
349 /* No key provided. Use default. */
350 if (key == NULL) {
351 key = __UNCONST(PW_POLICY_DEFAULTKEY);
352 goto load_policies;
355 (void)memset(buf, 0, sizeof(buf));
357 errno = 0;
358 switch (how) {
359 case PW_POLICY_BYSTRING:
361 * Check for provided key. If non-existant, fallback to
362 * the default.
364 pw_getconf(buf, sizeof(buf), key, "foo");
365 if (errno == ENOTDIR)
366 key = __UNCONST(PW_POLICY_DEFAULTKEY);
368 break;
370 case PW_POLICY_BYPASSWD: {
371 struct passwd *pentry = key;
374 * Check for policy for given user. If can't find any,
375 * try a policy for the login class. If can't find any,
376 * fallback to the default.
379 pw_getconf(buf, sizeof(buf), pentry->pw_name, "foo");
380 if (errno == ENOTDIR) {
381 memset(buf, 0, sizeof(buf));
382 pw_getconf(buf, sizeof(buf), pentry->pw_class, "foo");
383 if (errno == ENOTDIR)
384 key = __UNCONST(PW_POLICY_DEFAULTKEY);
385 else
386 key = pentry->pw_class;
387 } else
388 key = pentry->pw_name;
390 break;
393 case PW_POLICY_BYGROUP: {
394 struct group *gentry = key;
397 * Check for policy for given group. If can't find any,
398 * fallback to the default.
400 pw_getconf(buf, sizeof(buf), gentry->gr_name, "foo");
401 if (errno == ENOTDIR)
402 key = __UNCONST(PW_POLICY_DEFAULTKEY);
403 else
404 key = gentry->gr_name;
406 break;
409 default:
411 * Fail the policy because we don't know how to parse the
412 * key we were passed.
414 errno = EINVAL;
415 return NULL;
418 load_policies:
419 policy = malloc(sizeof(struct pw_policy));
420 if (policy == NULL)
421 return NULL;
423 memset(policy, 0xff, sizeof(struct pw_policy));
425 hp = &handlers[0];
426 while (hp->name != NULL) {
427 int error;
429 (void)memset(buf, 0, sizeof(buf));
430 pw_getconf(buf, sizeof(buf), key, hp->name);
431 if (*buf) {
432 error = (*hp->handler)(policy, LOAD_POLICY, NULL, buf,
433 hp->arg2);
434 if (error) {
435 errno = error;
436 return NULL;
439 hp++;
442 return policy;
446 pw_policy_test(pw_policy_t policy, char *pw)
448 struct pw_policy_handler *hp;
450 if (policy == NULL) {
451 errno = EINVAL;
452 return 0;
455 hp = &handlers[0];
456 while (hp->name != NULL) {
457 int error;
458 error = (*hp->handler)(policy, TEST_POLICY, pw, NULL, hp->arg2);
459 if (error) {
460 errno = error;
461 return -1;
463 hp++;
466 return 0;
469 void
470 pw_policy_free(pw_policy_t policy)
472 _DIAGASSERT(policy != NULL);
474 free(policy);