2 * Copyright (c) 2001-2003 Networks Associates Technology, Inc.
3 * Copyright (c) 2004-2007 Dag-Erling Smørgrav
6 * This software was developed for the FreeBSD Project by ThinkSec AS and
7 * Network Associates Laboratories, the Security Research Division of
8 * Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
9 * ("CBOSS"), as part of the DARPA CHATS research program.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote
20 * products derived from this software without specific prior written
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * $Id: openpam_configure.c,v 1.5 2008/01/27 01:22:59 christos Exp $
44 #include <security/pam_appl.h>
46 #include "openpam_impl.h"
48 const char *_pam_facility_name
[PAM_NUM_FACILITIES
] = {
49 [PAM_ACCOUNT
] = "account",
51 [PAM_PASSWORD
] = "password",
52 [PAM_SESSION
] = "session",
55 const char *_pam_control_flag_name
[PAM_NUM_CONTROL_FLAGS
] = {
56 [PAM_BINDING
] = "binding",
57 [PAM_OPTIONAL
] = "optional",
58 [PAM_REQUIRED
] = "required",
59 [PAM_REQUISITE
] = "requisite",
60 [PAM_SUFFICIENT
] = "sufficient",
63 static int openpam_load_chain(pam_handle_t
*, const char *, pam_facility_t
);
66 * Matches a word against the first one in a string.
67 * Returns non-zero if they match.
70 match_word(const char *str
, const char *word
)
73 while (*str
&& tolower((unsigned char)*str
) == tolower((unsigned char)*word
))
75 return (*str
== ' ' && *word
== '\0');
79 * Return a pointer to the next word (or the final NUL) in a string.
82 next_word(const char *str
)
85 /* skip current word */
86 while (*str
&& *str
!= ' ')
95 * Return a malloc()ed copy of the first word in a string.
98 dup_word(const char *str
)
103 for (end
= str
; *end
&& *end
!= ' '; ++end
)
105 if (asprintf(&word
, "%.*s", (int)(end
- str
), str
) < 0)
111 * Return the length of the first word in a string.
114 wordlen(const char *str
)
118 for (i
= 0; str
[i
] && str
[i
] != ' '; ++i
)
123 typedef enum { pam_conf_style
, pam_d_style
} openpam_style_t
;
126 * Extracts given chains from a policy file.
129 openpam_read_chain(pam_handle_t
*pamh
,
131 pam_facility_t facility
,
132 const char *filename
,
133 openpam_style_t style
)
135 pam_chain_t
*this, **next
;
137 int count
, i
, lineno
, ret
;
143 if ((f
= fopen(filename
, "r")) == NULL
) {
144 openpam_log(errno
== ENOENT
? PAM_LOG_DEBUG
: PAM_LOG_NOTICE
,
150 while ((line
= openpam_readline(f
, &lineno
, NULL
)) != NULL
) {
153 /* match service name */
154 if (style
== pam_conf_style
) {
155 if (!match_word(p
, service
)) {
162 /* match facility name */
163 for (fclt
= 0; fclt
< PAM_NUM_FACILITIES
; ++fclt
)
164 if (match_word(p
, _pam_facility_name
[fclt
]))
166 if (fclt
== PAM_NUM_FACILITIES
) {
167 openpam_log(PAM_LOG_NOTICE
,
168 "%s(%d): invalid facility '%.*s' (ignored)",
169 filename
, lineno
, wordlen(p
), p
);
172 if (facility
!= fclt
&& facility
!= PAM_FACILITY_ANY
) {
178 /* include other chain */
179 if (match_word(p
, "include")) {
181 if (*next_word(p
) != '\0')
182 openpam_log(PAM_LOG_NOTICE
,
183 "%s(%d): garbage at end of 'include' line",
185 if ((name
= dup_word(p
)) == NULL
)
187 ret
= openpam_load_chain(pamh
, name
, fclt
);
196 /* allocate new entry */
197 if ((this = calloc((size_t)1, sizeof *this)) == NULL
)
201 for (ctlf
= 0; ctlf
< PAM_NUM_CONTROL_FLAGS
; ++ctlf
)
202 if (match_word(p
, _pam_control_flag_name
[ctlf
]))
204 if (ctlf
== PAM_NUM_CONTROL_FLAGS
) {
205 openpam_log(PAM_LOG_ERROR
,
206 "%s(%d): invalid control flag '%.*s'",
207 filename
, lineno
, wordlen(p
), p
);
215 openpam_log(PAM_LOG_ERROR
,
216 "%s(%d): missing module name",
220 if ((name
= dup_word(p
)) == NULL
)
222 this->module
= openpam_load_module(name
);
224 if (this->module
== NULL
)
228 p
= q
= next_word(p
);
233 this->optv
= calloc((size_t)(this->optc
+ 1), sizeof(char *));
234 if (this->optv
== NULL
)
236 for (i
= 0; i
< this->optc
; ++i
) {
237 if ((this->optv
[i
] = dup_word(p
)) == NULL
)
243 for (next
= &pamh
->chains
[fclt
]; *next
!= NULL
;
244 next
= &(*next
)->next
)
258 openpam_log(PAM_LOG_ERROR
, "%s: %m", filename
);
266 static const char *openpam_policy_path
[] = {
270 "/usr/local/etc/pam.d/",
271 "/usr/local/etc/pam.conf",
273 /* Possibly /usr/pkg? */
279 * Locates the policy file for a given service and reads the given chains
283 openpam_load_chain(pam_handle_t
*pamh
,
285 pam_facility_t facility
)
292 for (path
= openpam_policy_path
; *path
!= NULL
; ++path
) {
294 if ((*path
)[len
- 1] == '/') {
295 if (asprintf(&filename
, "%s%s", *path
, service
) < 0) {
296 openpam_log(PAM_LOG_ERROR
, "asprintf(): %m");
297 return (-PAM_BUF_ERR
);
299 r
= openpam_read_chain(pamh
, service
, facility
,
300 filename
, pam_d_style
);
303 r
= openpam_read_chain(pamh
, service
, facility
,
304 *path
, pam_conf_style
);
315 * Configure a service
319 openpam_configure(pam_handle_t
*pamh
,
324 if (openpam_load_chain(pamh
, service
, PAM_FACILITY_ANY
) < 0)
327 for (fclt
= 0; fclt
< PAM_NUM_FACILITIES
; ++fclt
) {
328 if (pamh
->chains
[fclt
] != NULL
)
330 if (openpam_load_chain(pamh
, PAM_OTHER
, fclt
) < 0)
335 * On NetBSD we require the AUTH chain to have a binding
336 * or a required module.
339 pam_chain_t
*this = pamh
->chains
[PAM_AUTH
];
340 for (; this != NULL
; this = this->next
)
341 if (this->flag
== PAM_BINDING
||
342 this->flag
== PAM_REQUIRED
)
345 openpam_log(PAM_LOG_ERROR
,
346 "No required or binding component "
347 "in service %s, facility %s",
348 service
, _pam_facility_name
[PAM_AUTH
]);
353 return (PAM_SUCCESS
);
355 openpam_clear_chains(pamh
->chains
);
356 return (PAM_SYSTEM_ERR
);