1 /* $NetBSD: pw_policy.c,v 1.13 2007/01/09 14:04:44 elad Exp $ */
4 * Copyright (c) 2005, 2006 Elad Efrat <elad@NetBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
43 #include <machine/int_limits.h>
45 #define PW_POLICY_DEFAULTKEY "pw_policy"
50 #define MINMAX_ERR(min, max, n) ((min == 0 || max == 0) && n != 0) || \
51 (min > 0 && min > 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
);
82 struct pw_policy_handler
{
84 int (*handler
)(HANDLER_PROTO
);
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
},
100 pw_policy_parse_num(char *s
, int32_t *n
)
105 if (s
[0] == '*' && s
[1] == '\0') {
111 l
= strtol(s
, &endptr
, 10);
114 if (errno
== ERANGE
&& (l
== LONG_MIN
|| l
== LONG_MAX
))
117 if (l
< 0 || l
>= UINT16_MAX
)
126 pw_policy_parse_range(char *range
, int32_t *n
, int32_t *m
)
130 while (isspace((unsigned char)*range
))
132 p
= &range
[strlen(range
) - 1];
133 while (isspace((unsigned char)*p
))
136 /* Single characters: * = any number, 0 = none. */
137 if (range
[0] == '0' && range
[1] == '\0') {
141 if (range
[0] == '*' && range
[1] == '\0') {
146 /* Get range, N-M. */
147 p
= strchr(range
, '-');
150 if (pw_policy_parse_num(range
, n
) != 0)
159 if (pw_policy_parse_num(range
, n
) != 0 ||
160 pw_policy_parse_num(p
, m
) != 0)
168 pw_policy_handle_len(HANDLER_ARGS __unused
)
174 /* Here, '0' and '*' mean the same. */
175 if (pw_policy_parse_range(arg
, &policy
->minlen
,
176 &policy
->maxlen
) != 0)
184 if ((policy
->minlen
> 0 && (int32_t)len
< policy
->minlen
) ||
185 (policy
->maxlen
> 0 && (int32_t)len
> policy
->maxlen
))
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
;
217 if ((pw_policy_parse_range(arg
, n
, m
) != 0))
225 if (ischarclass((unsigned char)*pw
++))
227 } while (*pw
!= '\0');
229 if (MINMAX_ERR(*n
, *m
, count
))
240 pw_policy_handle_nclasses(HANDLER_ARGS __unused
)
244 if (pw_policy_parse_range(arg
, &policy
->minclasses
,
245 &policy
->maxclasses
) != 0)
251 int have_lower
= 0, have_upper
= 0, have_digit
= 0,
255 while (*pw
!= '\0') {
256 if (islower((unsigned char)*pw
) && !have_lower
) {
259 } else if (isupper((unsigned char)*pw
) && !have_upper
) {
262 } else if (isdigit((unsigned char)*pw
) && !have_digit
) {
265 } else if (ispunct((unsigned char)*pw
) && !have_punct
) {
272 if (MINMAX_ERR(policy
->minclasses
, policy
->maxclasses
, nsets
))
283 pw_policy_handle_ntoggles(HANDLER_ARGS __unused
)
287 if (pw_policy_parse_range(arg
, &policy
->mintoggles
,
288 &policy
->maxtoggles
) != 0)
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
);
310 current_set
== previous_set
) {
313 if (current_ntoggles
> ntoggles
)
314 ntoggles
= current_ntoggles
;
316 current_ntoggles
= 1;
319 previous_set
= current_set
;
327 if (MINMAX_ERR(policy
->mintoggles
, policy
->maxtoggles
,
339 pw_policy_load(void *key
, int how
)
342 struct pw_policy_handler
*hp
;
345 /* If there's no /etc/passwd.conf, don't touch the policy. */
346 if (access(_PATH_PASSWD_CONF
, R_OK
) == -1)
349 /* No key provided. Use default. */
351 key
= __UNCONST(PW_POLICY_DEFAULTKEY
);
355 (void)memset(buf
, 0, sizeof(buf
));
359 case PW_POLICY_BYSTRING
:
361 * Check for provided key. If non-existant, fallback to
364 pw_getconf(buf
, sizeof(buf
), key
, "foo");
365 if (errno
== ENOTDIR
)
366 key
= __UNCONST(PW_POLICY_DEFAULTKEY
);
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
);
386 key
= pentry
->pw_class
;
388 key
= pentry
->pw_name
;
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
);
404 key
= gentry
->gr_name
;
411 * Fail the policy because we don't know how to parse the
412 * key we were passed.
419 policy
= malloc(sizeof(struct pw_policy
));
423 memset(policy
, 0xff, sizeof(struct pw_policy
));
426 while (hp
->name
!= NULL
) {
429 (void)memset(buf
, 0, sizeof(buf
));
430 pw_getconf(buf
, sizeof(buf
), key
, hp
->name
);
432 error
= (*hp
->handler
)(policy
, LOAD_POLICY
, NULL
, buf
,
446 pw_policy_test(pw_policy_t policy
, char *pw
)
448 struct pw_policy_handler
*hp
;
450 if (policy
== NULL
) {
456 while (hp
->name
!= NULL
) {
458 error
= (*hp
->handler
)(policy
, TEST_POLICY
, pw
, NULL
, hp
->arg2
);
470 pw_policy_free(pw_policy_t policy
)
472 _DIAGASSERT(policy
!= NULL
);