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

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

Add strlcat() for non-BSD systems.
strlcpy() needs to be static.

  • Property svn:keywords set to Id
File size: 10.2 KB
Line 
1/*-
2 * Copyright (c) 2001-2003 Networks Associates Technology, Inc.
3 * Copyright (c) 2004-2012 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 554 2012-04-02 09:46:48Z des $
36 */
37
38#ifdef HAVE_CONFIG_H
39# include "config.h"
40#endif
41
42#include <sys/param.h>
43
44#include <ctype.h>
45#include <errno.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49
50#include <security/pam_appl.h>
51
52#include "openpam_impl.h"
53#include "openpam_ctype.h"
54#include "openpam_strlcat.h"
55#include "openpam_strlcpy.h"
56
57static int openpam_load_chain(pam_handle_t *, const char *, pam_facility_t);
58
59/*
60 * Validate a service name.
61 *
62 * Returns a non-zero value if the argument points to a NUL-terminated
63 * string consisting entirely of characters in the POSIX portable filename
64 * character set, excluding the path separator character.
65 */
66static int
67valid_service_name(const char *name)
68{
69        const char *p;
70
71        for (p = name; *p != '\0'; ++p)
72                if (!is_pfcs(*p))
73                        return (0);
74        return (1);
75}
76
77/*
78 * Parse the facility name.
79 *
80 * Returns the corresponding pam_facility_t value, or -1 if the argument
81 * is not a valid facility name.
82 */
83static pam_facility_t
84parse_facility_name(const char *name)
85{
86        int i;
87
88        for (i = 0; i < PAM_NUM_FACILITIES; ++i)
89                if (strcmp(pam_facility_name[i], name) == 0)
90                        return (i);
91        return ((pam_facility_t)-1);
92}
93
94/*
95 * Parse the control flag.
96 *
97 * Returns the corresponding pam_control_t value, or -1 if the argument is
98 * not a valid control flag name.
99 */
100static pam_control_t
101parse_control_flag(const char *name)
102{
103        int i;
104
105        for (i = 0; i < PAM_NUM_CONTROL_FLAGS; ++i)
106                if (strcmp(pam_control_flag_name[i], name) == 0)
107                        return (i);
108        return ((pam_control_t)-1);
109}
110
111/*
112 * Validate a file name.
113 *
114 * Returns a non-zero value if the argument points to a NUL-terminated
115 * string consisting entirely of characters in the POSIX portable filename
116 * character set, including the path separator character.
117 */
118static int
119valid_filename(const char *name)
120{
121        const char *p;
122
123        for (p = name; *p != '\0'; ++p)
124                if (!is_pfcs(*p) && *p != '/')
125                        return (0);
126        return (1);
127}
128
129typedef enum { pam_conf_style, pam_d_style } openpam_style_t;
130
131/*
132 * Extracts given chains from a policy file.
133 *
134 * Returns the number of policy entries which were found for the specified
135 * service and facility, or -1 if a system error occurred or a syntax
136 * error was encountered.
137 */
138static int
139openpam_parse_chain(pam_handle_t *pamh,
140        const char *service,
141        pam_facility_t facility,
142        FILE *f,
143        const char *filename,
144        openpam_style_t style)
145{
146        pam_chain_t *this, **next;
147        pam_facility_t fclt;
148        pam_control_t ctlf;
149        char *name, *servicename, *modulename;
150        int count, lineno, ret, serrno;
151        char **wordv, *word;
152        int i, wordc;
153
154        count = 0;
155        this = NULL;
156        name = NULL;
157        lineno = 0;
158        wordc = 0;
159        wordv = NULL;
160        while ((wordv = openpam_readlinev(f, &lineno, &wordc)) != NULL) {
161                /* blank line? */
162                if (wordc == 0) {
163                        FREEV(wordc, wordv);
164                        continue;
165                }
166                i = 0;
167
168                /* check service name if necessary */
169                if (style == pam_conf_style &&
170                    strcmp(wordv[i++], service) != 0) {
171                        FREEV(wordc, wordv);
172                        continue;
173                }
174
175                /* check facility name */
176                if ((word = wordv[i++]) == NULL ||
177                    (fclt = parse_facility_name(word)) == (pam_facility_t)-1) {
178                        openpam_log(PAM_LOG_ERROR,
179                            "%s(%d): missing or invalid facility",
180                            filename, lineno);
181                        goto fail;
182                }
183                if (facility != fclt && facility != PAM_FACILITY_ANY) {
184                        FREEV(wordc, wordv);
185                        continue;
186                }
187
188                /* check for "include" */
189                if ((word = wordv[i++]) != NULL &&
190                    strcmp(word, "include") == 0) {
191                        if ((servicename = wordv[i++]) == NULL ||
192                            !valid_service_name(servicename)) {
193                                openpam_log(PAM_LOG_ERROR,
194                                    "%s(%d): missing or invalid service name",
195                                    filename, lineno);
196                                goto fail;
197                        }
198                        if (wordv[i] != NULL) {
199                                openpam_log(PAM_LOG_ERROR,
200                                    "%s(%d): garbage at end of line",
201                                    filename, lineno);
202                                goto fail;
203                        }
204                        ret = openpam_load_chain(pamh, servicename, fclt);
205                        FREEV(wordc, wordv);
206                        if (ret < 0)
207                                goto fail;
208                        continue;
209                }
210
211                /* get control flag */
212                if (word == NULL || /* same word we compared to "include" */
213                    (ctlf = parse_control_flag(word)) == (pam_control_t)-1) {
214                        openpam_log(PAM_LOG_ERROR,
215                            "%s(%d): missing or invalid control flag",
216                            filename, lineno);
217                        goto fail;
218                }
219
220                /* get module name */
221                if ((modulename = wordv[i++]) == NULL ||
222                    !valid_filename(modulename)) {
223                        openpam_log(PAM_LOG_ERROR,
224                            "%s(%d): missing or invalid module name",
225                            filename, lineno);
226                        goto fail;
227                }
228
229                /* allocate new entry */
230                if ((this = calloc(1, sizeof *this)) == NULL)
231                        goto syserr;
232                this->flag = ctlf;
233
234                /* load module */
235                if ((this->module = openpam_load_module(modulename)) == NULL)
236                        goto fail;
237
238                /*
239                 * The remaining items in wordv are the module's
240                 * arguments.  We could set this->optv = wordv + i, but
241                 * then free(this->optv) wouldn't work.  Instead, we free
242                 * the words we've already consumed, shift the rest up,
243                 * and clear the tail end of the array.
244                 */
245                this->optc = wordc - i;
246                for (i = 0; i < wordc - this->optc; ++i) {
247                        FREE(wordv[i]);
248                        wordv[i] = wordv[wordc - this->optc + i];
249                        wordv[wordc - this->optc + i] = NULL;
250                }
251                this->optv = wordv;
252                wordv = NULL;
253                wordc = 0;
254
255                /* hook it up */
256                for (next = &pamh->chains[fclt]; *next != NULL;
257                     next = &(*next)->next)
258                        /* nothing */ ;
259                *next = this;
260                this = NULL;
261                ++count;
262        }
263        /*
264         * The loop ended because openpam_readword() returned NULL, which
265         * can happen for four different reasons: an I/O error (ferror(f)
266         * is true), a memory allocation failure (ferror(f) is false,
267         * errno is non-zero)
268         */
269        if (ferror(f) || errno != 0)
270                goto syserr;
271        if (!feof(f))
272                goto fail;
273        fclose(f);
274        return (count);
275syserr:
276        serrno = errno;
277        openpam_log(PAM_LOG_ERROR, "%s: %m", filename);
278        errno = serrno;
279        /* fall through */
280fail:
281        serrno = errno;
282        if (this && this->optc && this->optv)
283                FREEV(this->optc, this->optv);
284        FREE(this);
285        FREEV(wordc, wordv);
286        FREE(wordv);
287        FREE(name);
288        fclose(f);
289        errno = serrno;
290        return (-1);
291}
292
293static const char *openpam_policy_path[] = {
294        "/etc/pam.d/",
295        "/etc/pam.conf",
296        "/usr/local/etc/pam.d/",
297        "/usr/local/etc/pam.conf",
298        NULL
299};
300
301/*
302 * Locates the policy file for a given service and reads the given chains
303 * from it.
304 *
305 * Returns the number of policy entries which were found for the specified
306 * service and facility, or -1 if a system error occurred or a syntax
307 * error was encountered.
308 */
309static int
310openpam_load_chain(pam_handle_t *pamh,
311        const char *service,
312        pam_facility_t facility)
313{
314        const char **path;
315        char filename[PATH_MAX];
316        size_t len;
317        openpam_style_t style;
318        FILE *f;
319        int ret, serrno;
320
321        if (!valid_service_name(service)) {
322                openpam_log(PAM_LOG_ERROR, "invalid service name");
323                errno = EINVAL;
324                RETURNN(-1);
325        }
326        ENTERS(facility < 0 ? "any" : pam_facility_name[facility]);
327        for (path = openpam_policy_path; *path != NULL; ++path) {
328                /* construct filename */
329                len = strlcpy(filename, *path, sizeof filename);
330                if (filename[len - 1] == '/') {
331                        len = strlcat(filename, service, sizeof filename);
332                        if (len >= sizeof filename) {
333                                errno = ENAMETOOLONG;
334                                RETURNN(-1);
335                        }
336                        style = pam_d_style;
337                } else {
338                        style = pam_conf_style;
339                }
340
341                /* attempt to open the file */
342                if ((f = fopen(filename, "r")) == NULL) {
343                        if (errno == ENOENT || errno == ENOTDIR) {
344                                openpam_log(PAM_LOG_DEBUG, "%s: %m", filename);
345                                continue;
346                        } else {
347                                serrno = errno;
348                                openpam_log(PAM_LOG_ERROR, "%s: %m", filename);
349                                errno = serrno;
350                                RETURNN(-1);
351                        }
352                } else {
353                        openpam_log(PAM_LOG_DEBUG, "found %s", filename);
354                }
355
356                /* verify type, ownership and permissions */
357                if (openpam_check_desc_owner_perms(filename, fileno(f)) != 0) {
358                        serrno = errno;
359                        fclose(f);
360                        errno = serrno;
361                        RETURNN(-1);
362                }
363
364                /* parse the file */
365                ret = openpam_parse_chain(pamh, service, facility,
366                    f, filename, style);
367
368                /* in pam.d style, an empty file counts as a hit */
369                if (ret != 0 || style == pam_d_style)
370                        RETURNN(ret);
371        }
372        RETURNN(0);
373}
374
375/*
376 * OpenPAM internal
377 *
378 * Configure a service
379 */
380
381int
382openpam_configure(pam_handle_t *pamh,
383        const char *service)
384{
385        pam_facility_t fclt;
386        const char *p;
387        int serrno;
388
389        ENTERS(service);
390        for (p = service; *p; ++p)
391                if (!is_pfcs(*p))
392                        RETURNC(PAM_SYSTEM_ERR);
393        if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) < 0)
394                goto load_err;
395        for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt) {
396                if (pamh->chains[fclt] != NULL)
397                        continue;
398                if (openpam_load_chain(pamh, PAM_OTHER, fclt) < 0)
399                        goto load_err;
400        }
401        RETURNC(PAM_SUCCESS);
402load_err:
403        serrno = errno;
404        openpam_clear_chains(pamh->chains);
405        errno = serrno;
406        RETURNC(PAM_SYSTEM_ERR);
407}
408
409/*
410 * NODOC
411 *
412 * Error codes:
413 *      PAM_SYSTEM_ERR
414 */
Note: See TracBrowser for help on using the repository browser.