Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / openpam / lib / openpam_configure.c
bloba03fda348b9d59b7553afc54f7fa3b33141211c8
1 /*-
2 * Copyright (c) 2001-2003 Networks Associates Technology, Inc.
3 * Copyright (c) 2004-2007 Dag-Erling Smørgrav
4 * All rights reserved.
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
13 * are met:
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
21 * permission.
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
33 * SUCH DAMAGE.
35 * $Id: openpam_configure.c,v 1.5 2008/01/27 01:22:59 christos Exp $
38 #include <ctype.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
44 #include <security/pam_appl.h>
46 #include "openpam_impl.h"
48 const char *_pam_facility_name[PAM_NUM_FACILITIES] = {
49 [PAM_ACCOUNT] = "account",
50 [PAM_AUTH] = "auth",
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.
69 static int
70 match_word(const char *str, const char *word)
73 while (*str && tolower((unsigned char)*str) == tolower((unsigned char)*word))
74 ++str, ++word;
75 return (*str == ' ' && *word == '\0');
79 * Return a pointer to the next word (or the final NUL) in a string.
81 static const char *
82 next_word(const char *str)
85 /* skip current word */
86 while (*str && *str != ' ')
87 ++str;
88 /* skip whitespace */
89 while (*str == ' ')
90 ++str;
91 return (str);
95 * Return a malloc()ed copy of the first word in a string.
97 static char *
98 dup_word(const char *str)
100 const char *end;
101 char *word;
103 for (end = str; *end && *end != ' '; ++end)
104 /* nothing */ ;
105 if (asprintf(&word, "%.*s", (int)(end - str), str) < 0)
106 return (NULL);
107 return (word);
111 * Return the length of the first word in a string.
113 static int
114 wordlen(const char *str)
116 int i;
118 for (i = 0; str[i] && str[i] != ' '; ++i)
119 /* nothing */ ;
120 return (i);
123 typedef enum { pam_conf_style, pam_d_style } openpam_style_t;
126 * Extracts given chains from a policy file.
128 static int
129 openpam_read_chain(pam_handle_t *pamh,
130 const char *service,
131 pam_facility_t facility,
132 const char *filename,
133 openpam_style_t style)
135 pam_chain_t *this, **next;
136 const char *p, *q;
137 int count, i, lineno, ret;
138 pam_facility_t fclt;
139 pam_control_t ctlf;
140 char *line, *name;
141 FILE *f;
143 if ((f = fopen(filename, "r")) == NULL) {
144 openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_NOTICE,
145 "%s: %m", filename);
146 return (0);
148 this = NULL;
149 count = lineno = 0;
150 while ((line = openpam_readline(f, &lineno, NULL)) != NULL) {
151 p = line;
153 /* match service name */
154 if (style == pam_conf_style) {
155 if (!match_word(p, service)) {
156 FREE(line);
157 continue;
159 p = next_word(p);
162 /* match facility name */
163 for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt)
164 if (match_word(p, _pam_facility_name[fclt]))
165 break;
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);
170 goto fail;
172 if (facility != fclt && facility != PAM_FACILITY_ANY) {
173 FREE(line);
174 continue;
176 p = next_word(p);
178 /* include other chain */
179 if (match_word(p, "include")) {
180 p = next_word(p);
181 if (*next_word(p) != '\0')
182 openpam_log(PAM_LOG_NOTICE,
183 "%s(%d): garbage at end of 'include' line",
184 filename, lineno);
185 if ((name = dup_word(p)) == NULL)
186 goto syserr;
187 ret = openpam_load_chain(pamh, name, fclt);
188 FREE(name);
189 if (ret < 0)
190 goto fail;
191 count += ret;
192 FREE(line);
193 continue;
196 /* allocate new entry */
197 if ((this = calloc((size_t)1, sizeof *this)) == NULL)
198 goto syserr;
200 /* control flag */
201 for (ctlf = 0; ctlf < PAM_NUM_CONTROL_FLAGS; ++ctlf)
202 if (match_word(p, _pam_control_flag_name[ctlf]))
203 break;
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);
208 goto fail;
210 this->flag = ctlf;
212 /* module name */
213 p = next_word(p);
214 if (*p == '\0') {
215 openpam_log(PAM_LOG_ERROR,
216 "%s(%d): missing module name",
217 filename, lineno);
218 goto fail;
220 if ((name = dup_word(p)) == NULL)
221 goto syserr;
222 this->module = openpam_load_module(name);
223 FREE(name);
224 if (this->module == NULL)
225 goto fail;
227 /* module options */
228 p = q = next_word(p);
229 while (*q != '\0') {
230 ++this->optc;
231 q = next_word(q);
233 this->optv = calloc((size_t)(this->optc + 1), sizeof(char *));
234 if (this->optv == NULL)
235 goto syserr;
236 for (i = 0; i < this->optc; ++i) {
237 if ((this->optv[i] = dup_word(p)) == NULL)
238 goto syserr;
239 p = next_word(p);
242 /* hook it up */
243 for (next = &pamh->chains[fclt]; *next != NULL;
244 next = &(*next)->next)
245 /* nothing */ ;
246 *next = this;
247 this = NULL;
248 ++count;
250 /* next please... */
251 FREE(line);
253 if (!feof(f))
254 goto syserr;
255 fclose(f);
256 return (count);
257 syserr:
258 openpam_log(PAM_LOG_ERROR, "%s: %m", filename);
259 fail:
260 FREE(this);
261 FREE(line);
262 fclose(f);
263 return (-1);
266 static const char *openpam_policy_path[] = {
267 "/etc/pam.d/",
268 "/etc/pam.conf",
269 #ifndef __NetBSD__
270 "/usr/local/etc/pam.d/",
271 "/usr/local/etc/pam.conf",
272 #else
273 /* Possibly /usr/pkg? */
274 #endif
275 NULL
279 * Locates the policy file for a given service and reads the given chains
280 * from it.
282 static int
283 openpam_load_chain(pam_handle_t *pamh,
284 const char *service,
285 pam_facility_t facility)
287 const char **path;
288 char *filename;
289 size_t len;
290 int r;
292 for (path = openpam_policy_path; *path != NULL; ++path) {
293 len = strlen(*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);
301 FREE(filename);
302 } else {
303 r = openpam_read_chain(pamh, service, facility,
304 *path, pam_conf_style);
306 if (r != 0)
307 return (r);
309 return (0);
313 * OpenPAM internal
315 * Configure a service
319 openpam_configure(pam_handle_t *pamh,
320 const char *service)
322 pam_facility_t fclt;
324 if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) < 0)
325 goto load_err;
327 for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt) {
328 if (pamh->chains[fclt] != NULL)
329 continue;
330 if (openpam_load_chain(pamh, PAM_OTHER, fclt) < 0)
331 goto load_err;
333 #ifdef __NetBSD__
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)
343 break;
344 if (this == NULL) {
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]);
349 goto load_err;
352 #endif
353 return (PAM_SUCCESS);
354 load_err:
355 openpam_clear_chains(pamh->chains);
356 return (PAM_SYSTEM_ERR);
360 * NODOC
362 * Error codes:
363 * PAM_SYSTEM_ERR