source: openpam/trunk/lib/pam_start.c @ 19

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

Use 'sizeof(foo)' instead of 'sizeof foo' even where it's not
required. Although style(9) doesn't say anything about it, this
seems to be the preferred form.

Sponsored by: DARPA, NAI Labs

  • Property svn:keywords set to Id LastChangedRevision HeadURL LastChangedDate LastChangedBy
File size: 8.9 KB
Line 
1/*-
2 * Copyright (c) 2002 Networks Associates Technologies, Inc.
3 * All rights reserved.
4 *
5 * This software was developed for the FreeBSD Project by ThinkSec AS and
6 * NAI Labs, the Security Research Division of Network Associates, Inc.
7 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8 * DARPA CHATS research program.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote
19 *    products derived from this software without specific prior written
20 *    permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $Id: pam_start.c 19 2002-02-02 18:04:31Z des $
35 */
36
37#include <sys/param.h>
38
39#include <ctype.h>
40#include <dlfcn.h>
41#include <errno.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45
46#include <security/pam_appl.h>
47
48#include "openpam_impl.h"
49
50static int _pam_configure_service(pam_handle_t *pamh, const char *service);
51
52/*
53 * XSSO 4.2.1
54 * XSSO 6 page 89
55 *
56 * Initiate a PAM transaction
57 */
58
59int
60pam_start(const char *service,
61        const char *user,
62        const struct pam_conv *pam_conv,
63        pam_handle_t **pamh)
64{
65        struct pam_handle *ph;
66        int r;
67
68        if ((ph = calloc(1, sizeof(*ph))) == NULL)
69                return (PAM_BUF_ERR);
70        if ((r = pam_set_item(ph, PAM_SERVICE, service)) != PAM_SUCCESS)
71                goto fail;
72        if ((r = pam_set_item(ph, PAM_USER, user)) != PAM_SUCCESS)
73                goto fail;
74        if ((r = pam_set_item(ph, PAM_CONV, pam_conv)) != PAM_SUCCESS)
75                goto fail;
76
77        if ((r = _pam_configure_service(ph, service)) != PAM_SUCCESS &&
78            r != PAM_BUF_ERR)
79                r = _pam_configure_service(ph, PAM_OTHER);
80        if (r != PAM_SUCCESS)
81                goto fail;
82
83        *pamh = ph;
84        openpam_log(PAM_LOG_DEBUG, "pam_start(\"%s\") succeeded", service);
85        return (PAM_SUCCESS);
86
87 fail:
88        pam_end(ph, r);
89        return (r);
90}
91
92/* XXX move to a different file */
93const char *_pam_sm_func_name[PAM_NUM_PRIMITIVES] = {
94        "pam_sm_acct_mgmt",
95        "pam_sm_authenticate",
96        "pam_sm_chauthtok",
97        "pam_sm_close_session",
98        "pam_sm_open_session",
99        "pam_sm_setcred"
100};
101
102static int
103_pam_add_module(pam_handle_t *pamh,
104        int chain,
105        int flag,
106        const char *modpath,
107        const char *options /* XXX */ __unused)
108{
109        pam_chain_t *module, *iterator;
110        int i;
111
112        /* fill in configuration data */
113        if ((module = malloc(sizeof(*module))) == NULL) {
114                openpam_log(PAM_LOG_ERROR, "malloc(): %m");
115                return (PAM_BUF_ERR);
116        }
117        if ((module->modpath = strdup(modpath)) == NULL) {
118                openpam_log(PAM_LOG_ERROR, "strdup(): %m");
119                free(module);
120                return (PAM_BUF_ERR);
121        }
122        module->flag = flag;
123        module->next = NULL;
124
125        /* load module and resolve symbols */
126        /*
127         * Each module is dlopen()'d once for evey time it occurs in
128         * any chain.  While the linker is smart enough to not load
129         * the same module more than once, it does waste space in the
130         * form of linker handles and pam_func structs.
131         *
132         * TODO: implement a central module cache and replace the
133         * array of pam_func structs in struct pam_chain with pointers
134         * to the appropriate entry in the module cache.
135         *
136         * TODO: move this code out into a separate file to hide the
137         * details of the module cache and linker API from this file.
138         */
139        if ((module->dlh = dlopen(modpath, RTLD_NOW)) == NULL) {
140                openpam_log(PAM_LOG_ERROR, "dlopen(): %s", dlerror());
141                free(module->modpath);
142                free(module);
143                return (PAM_OPEN_ERR);
144        }
145        for (i = 0; i < PAM_NUM_PRIMITIVES; ++i)
146                module->primitive[i] =
147                    dlsym(module->dlh, _pam_sm_func_name[i]);
148
149        if ((iterator = pamh->chains[chain]) != NULL) {
150                while (iterator->next != NULL)
151                        iterator = iterator->next;
152                iterator->next = module;
153        } else {
154                pamh->chains[chain] = module;
155        }
156        return (PAM_SUCCESS);
157}
158
159#define PAM_CONF_STYLE  0
160#define PAM_D_STYLE     1
161#define MAX_LINE_LEN    1024
162
163static int
164_pam_read_policy_file(pam_handle_t *pamh,
165        const char *service,
166        const char *filename,
167        int style)
168{
169        char buf[MAX_LINE_LEN], *p, *q;
170        int ch, chain, flag, line, n, r;
171        size_t len;
172        FILE *f;
173
174        n = 0;
175
176        if ((f = fopen(filename, "r")) == NULL) {
177                openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_NOTICE,
178                    "%s: %m", filename);
179                return (0);
180        }
181        openpam_log(PAM_LOG_DEBUG, "looking for '%s' in %s",
182            service, filename);
183
184        for (line = 1; fgets(buf, MAX_LINE_LEN, f) != NULL; ++line) {
185                if ((len = strlen(buf)) == 0)
186                        continue;
187
188                /* check for overflow */
189                if (buf[--len] != '\n' && !feof(f)) {
190                        openpam_log(PAM_LOG_ERROR, "%s: line %d too long",
191                            filename, line);
192                        openpam_log(PAM_LOG_ERROR, "%s: ignoring line %d",
193                            filename, line);
194                        while ((ch = fgetc(f)) != EOF)
195                                if (ch == '\n')
196                                        break;
197                        continue;
198                }
199
200                /* strip comments and trailing whitespace */
201                if ((p = strchr(buf, '#')) != NULL)
202                        len = p - buf ? p - buf - 1 : p - buf;
203                while (len > 0 && isspace(buf[len]))
204                        --len;
205                if (len == 0)
206                        continue;
207                buf[len] = '\0';
208                p = q = buf;
209
210                /* check service name */
211                if (style == PAM_CONF_STYLE) {
212                        for (q = p = buf; *q != '\0' && !isspace(*q); ++q)
213                                /* nothing */;
214                        if (*q == '\0')
215                                goto syntax_error;
216                        *q++ = '\0';
217                        if (strcmp(p, service) != 0)
218                                continue;
219                        openpam_log(PAM_LOG_DEBUG, "%s: line %d matches '%s'",
220                            filename, line, service);
221                }
222
223
224                /* get module type */
225                for (p = q; isspace(*p); ++p)
226                        /* nothing */;
227                for (q = p; *q != '\0' && !isspace(*q); ++q)
228                        /* nothing */;
229                if (q == p || *q == '\0')
230                        goto syntax_error;
231                *q++ = '\0';
232                if (strcmp(p, "auth") == 0) {
233                        chain = PAM_AUTH;
234                } else if (strcmp(p, "account") == 0) {
235                        chain = PAM_ACCOUNT;
236                } else if (strcmp(p, "session") == 0) {
237                        chain = PAM_SESSION;
238                } else if (strcmp(p, "password") == 0) {
239                        chain = PAM_PASSWORD;
240                } else {
241                        openpam_log(PAM_LOG_ERROR,
242                            "%s: invalid module type on line %d: '%s'",
243                            filename, line, p);
244                        continue;
245                }
246
247                /* get control flag */
248                for (p = q; isspace(*p); ++p)
249                        /* nothing */;
250                for (q = p; *q != '\0' && !isspace(*q); ++q)
251                        /* nothing */;
252                if (q == p || *q == '\0')
253                        goto syntax_error;
254                *q++ = '\0';
255                if (strcmp(p, "required") == 0) {
256                        flag = PAM_REQUIRED;
257                } else if (strcmp(p, "requisite") == 0) {
258                        flag = PAM_REQUISITE;
259                } else if (strcmp(p, "sufficient") == 0) {
260                        flag = PAM_SUFFICIENT;
261                } else if (strcmp(p, "optional") == 0) {
262                        flag = PAM_OPTIONAL;
263                } else {
264                        openpam_log(PAM_LOG_ERROR,
265                            "%s: invalid control flag on line %d: '%s'",
266                            filename, line, p);
267                        continue;
268                }
269
270                /* get module name */
271                for (p = q; isspace(*p); ++p)
272                        /* nothing */;
273                for (q = p; *q != '\0' && !isspace(*q); ++q)
274                        /* nothing */;
275                if (q == p)
276                        goto syntax_error;
277
278                /* get options */
279                if (*q != '\0') {
280                        *q++ = 0;
281                        while (isspace(*q))
282                                ++q;
283                }
284
285                /*
286                 * Finally, add the module at the end of the
287                 * appropriate chain and bump the counter.
288                 */
289                if ((r = _pam_add_module(pamh, chain, flag, p, q)) !=
290                    PAM_SUCCESS)
291                        return (-r);
292                ++n;
293                continue;
294 syntax_error:
295                openpam_log(PAM_LOG_ERROR, "%s: syntax error on line %d",
296                    filename, line);
297                openpam_log(PAM_LOG_DEBUG, "%s: line %d: [%s]",
298                    filename, line, q);
299                openpam_log(PAM_LOG_ERROR, "%s: ignoring line %d",
300                    filename, line);
301        }
302
303        if (ferror(f))
304                openpam_log(PAM_LOG_ERROR, "%s: %m", filename);
305
306        fclose(f);
307        return (n);
308}
309
310static const char *_pam_policy_path[] = {
311        "/etc/pam.d/",
312        "/etc/pam.conf",
313        "/usr/local/etc/pam.d/",
314        NULL
315};
316
317static int
318_pam_configure_service(pam_handle_t *pamh,
319        const char *service)
320{
321        const char **path;
322        char *filename;
323        size_t len;
324        int r;
325
326        for (path = _pam_policy_path; *path != NULL; ++path) {
327                len = strlen(*path);
328                if ((*path)[len - 1] == '/') {
329                        filename = malloc(len + strlen(service) + 1);
330                        if (filename == NULL) {
331                                openpam_log(PAM_LOG_ERROR, "malloc(): %m");
332                                return (PAM_BUF_ERR);
333                        }
334                        strcpy(filename, *path);
335                        strcat(filename, service);
336                        r = _pam_read_policy_file(pamh,
337                            service, filename, PAM_D_STYLE);
338                        free(filename);
339                } else {
340                        r = _pam_read_policy_file(pamh,
341                            service, *path, PAM_CONF_STYLE);
342                }
343                if (r < 0)
344                        return (-r);
345                if (r > 0)
346                        return (PAM_SUCCESS);
347        }
348
349        return (PAM_SYSTEM_ERR);
350}
Note: See TracBrowser for help on using the repository browser.