openpam_load.c revision 91100
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$ 35 */ 36 37#include <dlfcn.h> 38#include <stdlib.h> 39#include <string.h> 40 41#include <security/pam_appl.h> 42 43#include "openpam_impl.h" 44 45const char *_pam_sm_func_name[PAM_NUM_PRIMITIVES] = { 46 "pam_sm_authenticate", 47 "pam_sm_setcred", 48 "pam_sm_acct_mgmt", 49 "pam_sm_open_session", 50 "pam_sm_close_session", 51 "pam_sm_chauthtok" 52}; 53 54static pam_module_t *modules; 55 56/* 57 * Load a dynamic module, or locate a static one. Keep a list of 58 * previously found modules to speed up the process. 59 */ 60 61static pam_module_t * 62openpam_load_module(const char *path) 63{ 64 pam_module_t *module; 65 void *dlh; 66 int i; 67 68 /* check cache first */ 69 for (module = modules; module != NULL; module = module->next) 70 if (strcmp(module->path, path) == 0) 71 goto found; 72 73 /* nope; try to load */ 74 if ((dlh = dlopen(path, RTLD_NOW)) == NULL) { 75 openpam_log(PAM_LOG_ERROR, "dlopen(): %s", dlerror()); 76 } else { 77 if ((module = calloc(1, sizeof *module)) == NULL) 78 goto buf_err; 79 if ((module->path = strdup(path)) == NULL) 80 goto buf_err; 81 module->dlh = dlh; 82 for (i = 0; i < PAM_NUM_PRIMITIVES; ++i) 83 module->func[i] = dlsym(dlh, _pam_sm_func_name[i]); 84 } 85 openpam_log(PAM_LOG_DEBUG, "%s dynamic %s", 86 (module == NULL) ? "no" : "using", path); 87 88#ifdef OPENPAM_STATIC_MODULES 89 /* look for a static module */ 90 if (module == NULL && strchr(path, '/') == NULL) { 91 module = openpam_static(path); 92 openpam_log(PAM_LOG_DEBUG, "%s static %s", 93 (module == NULL) ? "no" : "using", path); 94 } 95#endif 96 if (module == NULL) 97 return (NULL); 98 module->next = modules; 99 module->prev = NULL; 100 modules = module; 101 found: 102 ++module->refcount; 103 return (module); 104 buf_err: 105 openpam_log(PAM_LOG_ERROR, "malloc(): %m"); 106 dlclose(dlh); 107 free(module); 108 return (NULL); 109} 110 111 112/* 113 * Release a module. 114 * XXX highly thread-unsafe 115 */ 116 117static void 118openpam_release_module(pam_module_t *module) 119{ 120 if (module == NULL) 121 return; 122 --module->refcount; 123 if (module->refcount > 0) 124 /* still in use */ 125 return; 126 if (module->refcount < 0) { 127 openpam_log(PAM_LOG_ERROR, "module %s has negative refcount", 128 module->path); 129 module->refcount = 0; 130 } 131 if (module->dlh == NULL) 132 /* static module */ 133 return; 134 dlclose(module->dlh); 135 if (module->prev != NULL) 136 module->prev->next = module->next; 137 if (module->next != NULL) 138 module->next->prev = module->prev; 139 free(module); 140} 141 142 143/* 144 * Destroy a chain, freeing all its links and releasing the modules 145 * they point to. 146 */ 147 148static void 149openpam_destroy_chain(pam_chain_t *chain) 150{ 151 if (chain == NULL) 152 return; 153 openpam_destroy_chain(chain->next); 154 chain->next = NULL; 155 while (chain->optc--) 156 free(chain->optv[chain->optc]); 157 free(chain->optv); 158 openpam_release_module(chain->module); 159 free(chain); 160} 161 162/* 163 * Add a module to a chain. 164 */ 165 166int 167openpam_add_module(pam_handle_t *pamh, 168 int chain, 169 int flag, 170 const char *modpath, 171 int optc, 172 const char *optv[]) 173{ 174 pam_chain_t *new, *iterator; 175 176 if ((new = calloc(1, sizeof *new)) == NULL) 177 goto buf_err; 178 if ((new->optv = malloc(sizeof(char *) * (optc + 1))) == NULL) 179 goto buf_err; 180 while (optc--) 181 if ((new->optv[new->optc++] = strdup(*optv++)) == NULL) 182 goto buf_err; 183 new->optv[new->optc] = NULL; 184 new->flag = flag; 185 if ((new->module = openpam_load_module(modpath)) == NULL) { 186 openpam_destroy_chain(new); 187 return (PAM_OPEN_ERR); 188 } 189 if ((iterator = pamh->chains[chain]) != NULL) { 190 while (iterator->next != NULL) 191 iterator = iterator->next; 192 iterator->next = new; 193 } else { 194 pamh->chains[chain] = new; 195 } 196 return (PAM_SUCCESS); 197 198 buf_err: 199 openpam_log(PAM_LOG_ERROR, "%m"); 200 openpam_destroy_chain(new); 201 return (PAM_BUF_ERR); 202} 203 204 205/* 206 * Clear the chains and release the modules 207 */ 208 209void 210openpam_clear_chains(pam_handle_t *pamh) 211{ 212 int i; 213 214 for (i = 0; i < PAM_NUM_CHAINS; ++i) 215 openpam_destroy_chain(pamh->chains[i]); 216} 217 218/* 219 * NOPARSE 220 */ 221