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