openpam_configure.c revision 115698
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 47const 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 54const 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 62static 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 */ 68static int 69match_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 */ 80static const char * 81next_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 */ 96static char * 97dup_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 */ 112static int 113wordlen(const char *str) 114{ 115 int i; 116 117 for (i = 0; str[i] && str[i] != ' '; ++i) 118 /* nothing */ ; 119 return (i); 120} 121 122typedef enum { pam_conf_style, pam_d_style } openpam_style_t; 123 124/* 125 * Extracts given chains from a policy file. 126 */ 127static int 128openpam_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 265static 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 */ 277static int 278openpam_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 313int 314openpam_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 */ 340