]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/openpam/lib/libpam/openpam_dynamic.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / openpam / lib / libpam / openpam_dynamic.c
1 /*-
2  * Copyright (c) 2002-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_dynamic.c 683 2013-04-14 14:49:59Z des $
36  */
37
38 #ifdef HAVE_CONFIG_H
39 # include "config.h"
40 #endif
41
42 #include <sys/param.h>
43
44 #include <dlfcn.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 #include <security/pam_appl.h>
53
54 #include "openpam_impl.h"
55 #include "openpam_asprintf.h"
56 #include "openpam_ctype.h"
57 #include "openpam_dlfunc.h"
58
59 #ifndef RTLD_NOW
60 #define RTLD_NOW RTLD_LAZY
61 #endif
62
63 /*
64  * OpenPAM internal
65  *
66  * Perform sanity checks and attempt to load a module
67  */
68
69 #ifdef HAVE_FDLOPEN
70 static void *
71 try_dlopen(const char *modfn)
72 {
73         void *dlh;
74         int fd;
75
76         openpam_log(PAM_LOG_LIBDEBUG, "dlopen(%s)", modfn);
77         if ((fd = open(modfn, O_RDONLY)) < 0) {
78                 if (errno != ENOENT)
79                         openpam_log(PAM_LOG_ERROR, "%s: %m", modfn);
80                 return (NULL);
81         }
82         if (OPENPAM_FEATURE(VERIFY_MODULE_FILE) &&
83             openpam_check_desc_owner_perms(modfn, fd) != 0) {
84                 close(fd);
85                 return (NULL);
86         }
87         if ((dlh = fdlopen(fd, RTLD_NOW)) == NULL) {
88                 openpam_log(PAM_LOG_ERROR, "%s: %s", modfn, dlerror());
89                 close(fd);
90                 errno = 0;
91                 return (NULL);
92         }
93         close(fd);
94         return (dlh);
95 }
96 #else
97 static void *
98 try_dlopen(const char *modfn)
99 {
100         int check_module_file;
101         void *dlh;
102
103         openpam_log(PAM_LOG_LIBDEBUG, "dlopen(%s)", modfn);
104         openpam_get_feature(OPENPAM_VERIFY_MODULE_FILE,
105             &check_module_file);
106         if (check_module_file &&
107             openpam_check_path_owner_perms(modfn) != 0)
108                 return (NULL);
109         if ((dlh = dlopen(modfn, RTLD_NOW)) == NULL) {
110                 openpam_log(PAM_LOG_ERROR, "%s: %s", modfn, dlerror());
111                 errno = 0;
112                 return (NULL);
113         }
114         return (dlh);
115 }
116 #endif
117
118 /*
119  * Try to load a module from the suggested location.
120  */
121 static pam_module_t *
122 try_module(const char *modpath)
123 {
124         const pam_module_t *dlmodule;
125         pam_module_t *module;
126         int i, serrno;
127
128         if ((module = calloc(1, sizeof *module)) == NULL ||
129             (module->path = strdup(modpath)) == NULL ||
130             (module->dlh = try_dlopen(modpath)) == NULL)
131                 goto err;
132         dlmodule = dlsym(module->dlh, "_pam_module");
133         for (i = 0; i < PAM_NUM_PRIMITIVES; ++i) {
134                 if (dlmodule) {
135                         module->func[i] = dlmodule->func[i];
136                 } else {
137                         module->func[i] = (pam_func_t)dlfunc(module->dlh,
138                             pam_sm_func_name[i]);
139                         /*
140                          * This openpam_log() call is a major source of
141                          * log spam, and the cases that matter are caught
142                          * and logged in openpam_dispatch().  This would
143                          * be less problematic if dlerror() returned an
144                          * error code so we could log an error only when
145                          * dlfunc() failed for a reason other than "no
146                          * such symbol".
147                          */
148 #if 0
149                         if (module->func[i] == NULL)
150                                 openpam_log(PAM_LOG_LIBDEBUG, "%s: %s(): %s",
151                                     modpath, pam_sm_func_name[i], dlerror());
152 #endif
153                 }
154         }
155         return (module);
156 err:
157         serrno = errno;
158         if (module != NULL) {
159                 if (module->dlh != NULL)
160                         dlclose(module->dlh);
161                 if (module->path != NULL)
162                         FREE(module->path);
163                 FREE(module);
164         }
165         errno = serrno;
166         if (serrno != 0 && serrno != ENOENT)
167                 openpam_log(PAM_LOG_ERROR, "%s: %m", modpath);
168         errno = serrno;
169         return (NULL);
170 }
171
172 /*
173  * OpenPAM internal
174  *
175  * Locate a dynamically linked module
176  */
177
178 pam_module_t *
179 openpam_dynamic(const char *modname)
180 {
181         pam_module_t *module;
182         char modpath[PATH_MAX];
183         const char **path, *p;
184         int has_so, has_ver;
185         int dot, len;
186
187         /*
188          * Simple case: module name contains path separator(s)
189          */
190         if (strchr(modname, '/') != NULL) {
191                 /*
192                  * Absolute paths are not allowed if RESTRICT_MODULE_NAME
193                  * is in effect (default off).  Relative paths are never
194                  * allowed.
195                  */
196                 if (OPENPAM_FEATURE(RESTRICT_MODULE_NAME) ||
197                     modname[0] != '/') {
198                         openpam_log(PAM_LOG_ERROR,
199                             "invalid module name: %s", modname);
200                         return (NULL);
201                 }
202                 return (try_module(modname));
203         }
204
205         /*
206          * Check for .so and version sufixes
207          */
208         p = strchr(modname, '\0');
209         has_ver = has_so = 0;
210         while (is_digit(*p))
211                 --p;
212         if (*p == '.' && *++p != '\0') {
213                 /* found a numeric suffix */
214                 has_ver = 1;
215                 /* assume that .so is either present or unneeded */
216                 has_so = 1;
217         } else if (*p == '\0' && p >= modname + sizeof PAM_SOEXT &&
218             strcmp(p - sizeof PAM_SOEXT + 1, PAM_SOEXT) == 0) {
219                 /* found .so suffix */
220                 has_so = 1;
221         }
222
223         /*
224          * Complicated case: search for the module in the usual places.
225          */
226         for (path = openpam_module_path; *path != NULL; ++path) {
227                 /*
228                  * Assemble the full path, including the version suffix.  Take
229                  * note of where the suffix begins so we can cut it off later.
230                  */
231                 if (has_ver)
232                         len = snprintf(modpath, sizeof modpath, "%s/%s%n",
233                             *path, modname, &dot);
234                 else if (has_so)
235                         len = snprintf(modpath, sizeof modpath, "%s/%s%n.%d",
236                             *path, modname, &dot, LIB_MAJ);
237                 else
238                         len = snprintf(modpath, sizeof modpath, "%s/%s%s%n.%d",
239                             *path, modname, PAM_SOEXT, &dot, LIB_MAJ);
240                 /* check for overflow */
241                 if (len < 0 || (unsigned int)len >= sizeof modpath) {
242                         errno = ENOENT;
243                         continue;
244                 }
245                 /* try the versioned path */
246                 if ((module = try_module(modpath)) != NULL)
247                         return (module);
248                 if (errno == ENOENT && modpath[dot] != '\0') {
249                         /* no luck, try the unversioned path */
250                         modpath[dot] = '\0';
251                         if ((module = try_module(modpath)) != NULL)
252                                 return (module);
253                 }
254         }
255
256         /* :( */
257         return (NULL);
258 }
259
260 /*
261  * NOPARSE
262  */