source: openpam/trunk/lib/openpam_configure.c @ 437

Last change on this file since 437 was 437, checked in by Dag-Erling Smørgrav, 9 years ago

Update copyright and release notes.

  • Property svn:keywords set to Id
File size: 8.0 KB
Line 
1/*-
2 * Copyright (c) 2001-2003 Networks Associates Technology, Inc.
3 * Copyright (c) 2004-2011 Dag-Erling Smørgrav
4 * All rights reserved.
5 *
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.
10 *
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.
22 *
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.
34 *
35 * $Id: openpam_configure.c 437 2011-09-13 12:00:13Z des $
36 */
37
38#ifdef HAVE_CONFIG_H
39# include "config.h"
40#endif
41
42#include <ctype.h>
43#include <errno.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47
48#include <security/pam_appl.h>
49
50#include "openpam_impl.h"
51
52const char *_pam_facility_name[PAM_NUM_FACILITIES] = {
53        [PAM_ACCOUNT]           = "account",
54        [PAM_AUTH]              = "auth",
55        [PAM_PASSWORD]          = "password",
56        [PAM_SESSION]           = "session",
57};
58
59const char *_pam_control_flag_name[PAM_NUM_CONTROL_FLAGS] = {
60        [PAM_BINDING]           = "binding",
61        [PAM_OPTIONAL]          = "optional",
62        [PAM_REQUIRED]          = "required",
63        [PAM_REQUISITE]         = "requisite",
64        [PAM_SUFFICIENT]        = "sufficient",
65};
66
67static int openpam_load_chain(pam_handle_t *, const char *, pam_facility_t);
68
69/*
70 * Matches a word against the first one in a string.
71 * Returns non-zero if they match.
72 */
73static int
74match_word(const char *str, const char *word)
75{
76
77        while (*str && tolower(*str) == tolower(*word))
78                ++str, ++word;
79        return (*str == ' ' && *word == '\0');
80}
81
82/*
83 * Return a pointer to the next word (or the final NUL) in a string.
84 */
85static const char *
86next_word(const char *str)
87{
88
89        /* skip current word */
90        while (*str && *str != ' ')
91                ++str;
92        /* skip whitespace */
93        while (*str == ' ')
94                ++str;
95        return (str);
96}
97
98/*
99 * Return a malloc()ed copy of the first word in a string.
100 */
101static char *
102dup_word(const char *str)
103{
104        const char *end;
105        char *word;
106
107        for (end = str; *end && *end != ' '; ++end)
108                /* nothing */ ;
109        if (asprintf(&word, "%.*s", (int)(end - str), str) < 0)
110                return (NULL);
111        return (word);
112}
113
114/*
115 * Return the length of the first word in a string.
116 */
117static int
118wordlen(const char *str)
119{
120        int i;
121
122        for (i = 0; str[i] && str[i] != ' '; ++i)
123                /* nothing */ ;
124        return (i);
125}
126
127typedef enum { pam_conf_style, pam_d_style } openpam_style_t;
128
129/*
130 * Extracts given chains from a policy file.
131 */
132static int
133openpam_read_chain(pam_handle_t *pamh,
134        const char *service,
135        pam_facility_t facility,
136        const char *filename,
137        openpam_style_t style)
138{
139        pam_chain_t *this, **next;
140        const char *p, *q;
141        int count, i, lineno, ret;
142        pam_facility_t fclt;
143        pam_control_t ctlf;
144        char *line, *name;
145        FILE *f;
146
147        if ((f = fopen(filename, "r")) == NULL) {
148                openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_NOTICE,
149                    "%s: %m", filename);
150                return (0);
151        }
152        this = NULL;
153        count = lineno = 0;
154        while ((line = openpam_readline(f, &lineno, NULL)) != NULL) {
155                p = line;
156
157                /* match service name */
158                if (style == pam_conf_style) {
159                        if (!match_word(p, service)) {
160                                FREE(line);
161                                continue;
162                        }
163                        p = next_word(p);
164                }
165
166                /* match facility name */
167                for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt)
168                        if (match_word(p, _pam_facility_name[fclt]))
169                                break;
170                if (fclt == PAM_NUM_FACILITIES) {
171                        openpam_log(PAM_LOG_NOTICE,
172                            "%s(%d): invalid facility '%.*s' (ignored)",
173                            filename, lineno, wordlen(p), p);
174                        goto fail;
175                }
176                if (facility != fclt && facility != PAM_FACILITY_ANY) {
177                        FREE(line);
178                        continue;
179                }
180                p = next_word(p);
181
182                /* include other chain */
183                if (match_word(p, "include")) {
184                        p = next_word(p);
185                        if (*next_word(p) != '\0')
186                                openpam_log(PAM_LOG_NOTICE,
187                                    "%s(%d): garbage at end of 'include' line",
188                                    filename, lineno);
189                        if ((name = dup_word(p)) == NULL)
190                                goto syserr;
191                        ret = openpam_load_chain(pamh, name, fclt);
192                        FREE(name);
193                        if (ret < 0)
194                                goto fail;
195                        count += ret;
196                        FREE(line);
197                        continue;
198                }
199
200                /* allocate new entry */
201                if ((this = calloc(1, sizeof *this)) == NULL)
202                        goto syserr;
203
204                /* control flag */
205                for (ctlf = 0; ctlf < PAM_NUM_CONTROL_FLAGS; ++ctlf)
206                        if (match_word(p, _pam_control_flag_name[ctlf]))
207                                break;
208                if (ctlf == PAM_NUM_CONTROL_FLAGS) {
209                        openpam_log(PAM_LOG_ERROR,
210                            "%s(%d): invalid control flag '%.*s'",
211                            filename, lineno, wordlen(p), p);
212                        goto fail;
213                }
214                this->flag = ctlf;
215
216                /* module name */
217                p = next_word(p);
218                if (*p == '\0') {
219                        openpam_log(PAM_LOG_ERROR,
220                            "%s(%d): missing module name",
221                            filename, lineno);
222                        goto fail;
223                }
224                if ((name = dup_word(p)) == NULL)
225                        goto syserr;
226                this->module = openpam_load_module(name);
227                FREE(name);
228                if (this->module == NULL)
229                        goto fail;
230
231                /* module options */
232                p = q = next_word(p);
233                while (*q != '\0') {
234                        ++this->optc;
235                        q = next_word(q);
236                }
237                this->optv = calloc(this->optc + 1, sizeof(char *));
238                if (this->optv == NULL)
239                        goto syserr;
240                for (i = 0; i < this->optc; ++i) {
241                        if ((this->optv[i] = dup_word(p)) == NULL)
242                                goto syserr;
243                        p = next_word(p);
244                }
245
246                /* hook it up */
247                for (next = &pamh->chains[fclt]; *next != NULL;
248                     next = &(*next)->next)
249                        /* nothing */ ;
250                *next = this;
251                this = NULL;
252                ++count;
253
254                /* next please... */
255                FREE(line);
256        }
257        if (!feof(f))
258                goto syserr;
259        fclose(f);
260        return (count);
261syserr:
262        openpam_log(PAM_LOG_ERROR, "%s: %m", filename);
263fail:
264        FREE(this);
265        FREE(line);
266        fclose(f);
267        return (-1);
268}
269
270static const char *openpam_policy_path[] = {
271        "/etc/pam.d/",
272        "/etc/pam.conf",
273        "/usr/local/etc/pam.d/",
274        "/usr/local/etc/pam.conf",
275        NULL
276};
277
278/*
279 * Locates the policy file for a given service and reads the given chains
280 * from it.
281 */
282static int
283openpam_load_chain(pam_handle_t *pamh,
284        const char *service,
285        pam_facility_t facility)
286{
287        const char **path;
288        char *filename;
289        size_t len;
290        int r;
291
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);
298                        }
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);
305                }
306                if (r != 0)
307                        return (r);
308        }
309        return (0);
310}
311
312/*
313 * OpenPAM internal
314 *
315 * Configure a service
316 */
317
318int
319openpam_configure(pam_handle_t *pamh,
320        const char *service)
321{
322        pam_facility_t fclt;
323
324        if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) < 0)
325                goto load_err;
326
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;
332        }
333        return (PAM_SUCCESS);
334load_err:
335        openpam_clear_chains(pamh->chains);
336        return (PAM_SYSTEM_ERR);
337}
338
339/*
340 * NODOC
341 *
342 * Error codes:
343 *      PAM_SYSTEM_ERR
344 */
Note: See TracBrowser for help on using the repository browser.