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