1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
|
.\" $NetBSD: pw_policy.3,v 1.8 2006/03/23 19:31:37 wiz Exp $
.\"
.\" Copyright 2005, 2006 Elad Efrat <elad@NetBSD.org>
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. The name of the Author may not be used to endorse or promote
.\" products derived from this software without specific prior written
.\" permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd March 19, 2006
.Dt PW_POLICY 3
.Os
.Sh NAME
.Nm pw_policy_load ,
.Nm pw_policy_test
.Nd password policy enforcement
.Sh LIBRARY
.Lb libutil
.Sh SYNOPSIS
.In util.h
.Ft pw_policy_t
.Fn pw_policy_load "void *key" "int how"
.Ft int
.Fn pw_policy_test "pw_policy_t policy" "char *pw"
.Ft void
.Fn pw_policy_free "pw_policy_t policy"
.Sh DESCRIPTION
The
.Fn pw_policy_load ,
.Fn pw_policy_test ,
and
.Fn pw_policy_free
functions are used as an interface to the system's password policy
as specified in
.Pa /etc/passwd.conf .
.Pp
.Fn pw_policy_load
will load a password policy and return a pointer to a
.Ar pw_policy_t
containing it.
It is the caller's responsibility to free this pointer using
.Fn pw_policy_free .
.Pp
Using
.Xr pw_getconf 3
terminology,
.Fn pw_policy_load
accepts a
.Ar key
to be used when searching
.Pa /etc/passwd.conf
for a password policy.
This key contains various options describing different policies.
Some built-in ones are described along with their syntax below.
.Pp
To allow calling from various program contexts
and using various password policy retrieval schemes,
.Ar how
tells
.Fn pw_policy_load
how to treat
.Ar key .
.Pp
Possible values for
.Ar how
are:
.Pp
.Bl -tag -width kungfuninja -compact
.It Li PW_POLICY_BYSTRING
.Ar key
is used as a
.Ft char * ,
looking up the string it contains in
.Pa /etc/passwd.conf .
.Pp
.It Li PW_POLICY_BYPASSWD
.Ar key
is used as a
.Ft struct passwd * ,
first looking up the username in
.Ft pw_name ,
and if no key can be found, it will try the login class in
.Ft pw_class .
.Pp
.It Li PW_POLICY_BYGROUP
.Ar key
is used as a
.Ft struct group * ,
looking up the group name in
.Ft gr_name .
.El
.Pp
If
.Ar key
is
.Dv NULL ,
or no specified key can be found, the default key,
.Dq pw_policy ,
is used.
If even the default key can't be found,
the password is accepted as no policy is defined.
.Pp
.Fn pw_policy_test
can be used to check if the password in
.Ar pw
is compliant with the policy in
.Ar policy .
.Sh BUILT-IN POLICY SYNTAX
Available built-in policy options include the following:
.Pp
.Bl -tag -width kungfuninja -compact
.It length
Length of the password.
.It uppercase
Number of upper-case characters in the password.
.It lowercase
Number of lower-case characters in the password.
.It digits
Number of digits in the password.
.It punctuation
Number of punctuation characters in the password.
.It nclasses
Number of different character classes in the password.
.It ntoggles
How often a user has to toggle between character classes in the password.
.El
.Pp
Options are used inside keys.
An option uses a format of
.Dq option = value .
For the built-in options, we use either
.Dq N
or
.Dq N-M
for the value.
.Pp
The first,
.Dq N
format, specifies a single length.
For example, the following option specifies that the password should
have exactly 3 upper-case characters:
.Bd -literal -offset indent
uppercase = 3
.Ed
.Pp
The second,
.Dq N-M
format, can be used to specify a range.
Forcing a policy for number of digits between 1 and 4 would be:
.Bd -literal -offset indent
digits = 1-4
.Ed
.Pp
The characters
.Sq 0
and
.Sq *
can also be used to indicate
.Dq not allowed
and
.Dq any number ,
respectively.
To illustrate, the following example states that the number of
punctuation characters should be at least two:
.Bd -literal -offset indent
punctuation = 2-*
.Ed
.Pp
No more than 7 digits:
.Bd -literal -offset indent
digits = *-7
.Ed
.Pp
Any number of lower-case characters:
.Bd -literal -offset indent
lowercase = *
.Ed
.Pp
Upper-case characters not allowed:
.Bd -literal -offset indent
uppercase = 0
.Ed
.Pp
To specify that the password must be at least 8 characters long:
.Bd -literal -offset indent
length = 8-*
.Ed
.Pp
Specifying a password must have at least 3 different character classes:
.Bd -literal -offset indent
nclasses = 3-*
.Ed
.Pp
And that the user must change character class every 2 characters:
.Bd -literal -offset indent
ntoggles = *-2
.Ed
.Pp
Note that when using the
.Dq nclasses
directive, the policy will be initialized to allow any number of characters
from all classes.
If desired, this should be overridden after the
.Dq nclasses
option.
.Sh RETURN VALUES
.Fn pw_policy_load
returns a
.Ar pw_policy_t
on success, or
.Dv NULL
on failure, in which case the
.Va errno
variable will be set to any of the following values indicating the
reason for the failure:
.Bl -tag -width Er
.It Bq Er ENOENT
The
.Pa /etc/passwd.conf
is missing.
.It Bq Er EINVAL
Invalid value for the
.Ar how
parameter or an invalid value in the password policy specification.
.El
.Pp
.Fn pw_policy_load
can also set
.Va errno
to a value returned by the called handlers and/or
.Xr malloc 3 .
.Pp
.Fn pw_policy_test
returns 0 if the password follows the policy, or \-1 if it doesn't,
.Va errno
can be set to any of the following values:
.Bl -tag -width Er
.It Bq Er EPERM
The password does not follow the password policy.
.It Bq Er EINVAL
.Dv NULL
pointer was passed as the password.
.El
.Pp
In addition,
.Va errno
can be set to any error code returned by the handlers.
.Sh FILES
.Bl -tag -width /etc/passwd.conf -compact
.It Pa /etc/passwd.conf
password configuration file.
.El
.Sh EXAMPLES
Declare a password policy storage:
.Bd -literal -offset indent
pw_policy_t policy;
.Ed
.Pp
Load the system global password policy into
.Ar policy :
.Bd -literal -offset indent
policy = pw_policy_load(NULL, 0);
if (policy == NULL)
errx(1, "Can't load password policy");
.Ed
.Pp
Load a policy for a user whose password database entry is in
.Ar pw_entry
into
.Ar policy :
.Bd -literal -offset indent
policy = pw_policy_load(pw_entry, PW_POLICY_BYPASSWD);
if (policy == NULL)
errx(1, "Can't load password policy for \e"%s\e"", pw_entry-\*[Gt]pw_name);
.Ed
.Pp
Note that
.Fn pw_policy_load
will first look for a password policy for the username in
.Ar pw_entry-\*[Gt]pw_name ,
if not found, it will try looking for a policy for the login class in
.Ar pw_entry-\*[Gt]pw_class ,
and if it can't find such either it will fallback to the default key,
.Dq pw_policy .
.Pp
Load the password policy for a group
whose group database entry is in
.Ar grent ,
into
.Ar policy :
.Bd -literal -offset indent
policy = pw_policy_load(grent, PW_POLICY_BYGROUP);
if (policy == NULL)
errx(1, "Can't load password policy for \e"%s\e"", grent-\*[Gt]gr_name);
.Ed
.Pp
Check if
.Ar the_password
follows the policy in
.Ar policy :
.Bd -literal -offset indent
if (pw_policy_test(policy, the_password) != 0)
warnx("Please refer to the password policy");
.Ed
.Pp
After finished using the password policy, free it:
.Bd -literal -offset indent
pw_policy_free(policy);
.Ed
.Pp
An example for a common default password policy in
.Pa /etc/passwd.conf :
.Bd -literal -offset indent
pw_policy:
length = 8-* # At least 8 characters long,
lowercase = 1-* # combining lowercase,
uppercase = 1-* # uppercase,
digits = 1-* # and digits.
punctuation = * # Punctuation is optional.
.Ed
.Pp
A different policy that might be used:
.Bd -literal -offset indent
nclasses = 3-* # At least 3 different character classes,
ntoggles = *-2 # not more than 2 same class in a row.
.Ed
.Sh SEE ALSO
.Xr pw_getconf 3 ,
.Xr passwd.conf 5
.Sh HISTORY
The
.Fn pw_policy_load ,
.Fn pw_policy_test ,
and
.Fn pw_policy_free
functions first appeared in
.Nx 4.0 .
.Sh AUTHORS
.An Elad Efrat
.Aq elad@NetBSD.org
|