openpam_configure.c revision 107937
1/*- 2 * Copyright (c) 2002 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#6 $ 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#define PAM_CONF_STYLE 0 48#define PAM_D_STYLE 1 49#define MAX_LINE_LEN 1024 50#define MAX_OPTIONS 256 51 52static int 53openpam_read_policy_file(pam_chain_t *policy[], 54 const char *service, 55 const char *filename, 56 int style) 57{ 58 char buf[MAX_LINE_LEN], *p, *q; 59 const char *optv[MAX_OPTIONS + 1]; 60 int ch, chain, flag, line, optc, n, r; 61 size_t len; 62 FILE *f; 63 64 n = 0; 65 66 if ((f = fopen(filename, "r")) == NULL) { 67 openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_NOTICE, 68 "%s: %m", filename); 69 return (0); 70 } 71 openpam_log(PAM_LOG_DEBUG, "looking for '%s' in %s", 72 service, filename); 73 74 for (line = 1; fgets(buf, MAX_LINE_LEN, f) != NULL; ++line) { 75 if ((len = strlen(buf)) == 0) 76 continue; 77 78 /* check for overflow */ 79 if (buf[--len] != '\n' && !feof(f)) { 80 openpam_log(PAM_LOG_ERROR, "%s: line %d too long", 81 filename, line); 82 openpam_log(PAM_LOG_ERROR, "%s: ignoring line %d", 83 filename, line); 84 while ((ch = fgetc(f)) != EOF) 85 if (ch == '\n') 86 break; 87 continue; 88 } 89 90 /* strip comments and trailing whitespace */ 91 if ((p = strchr(buf, '#')) != NULL) 92 len = p - buf ? p - buf - 1 : p - buf; 93 while (len > 0 && isspace(buf[len - 1])) 94 --len; 95 if (len == 0) 96 continue; 97 buf[len] = '\0'; 98 p = q = buf; 99 100 /* check service name */ 101 if (style == PAM_CONF_STYLE) { 102 for (q = p = buf; *q != '\0' && !isspace(*q); ++q) 103 /* nothing */; 104 if (*q == '\0') 105 goto syntax_error; 106 *q++ = '\0'; 107 if (strcmp(p, service) != 0) 108 continue; 109 openpam_log(PAM_LOG_DEBUG, "%s: line %d matches '%s'", 110 filename, line, service); 111 } 112 113 114 /* get module type */ 115 for (p = q; isspace(*p); ++p) 116 /* nothing */; 117 for (q = p; *q != '\0' && !isspace(*q); ++q) 118 /* nothing */; 119 if (q == p || *q == '\0') 120 goto syntax_error; 121 *q++ = '\0'; 122 if (strcmp(p, "auth") == 0) { 123 chain = PAM_AUTH; 124 } else if (strcmp(p, "account") == 0) { 125 chain = PAM_ACCOUNT; 126 } else if (strcmp(p, "session") == 0) { 127 chain = PAM_SESSION; 128 } else if (strcmp(p, "password") == 0) { 129 chain = PAM_PASSWORD; 130 } else { 131 openpam_log(PAM_LOG_ERROR, 132 "%s: invalid module type on line %d: '%s'", 133 filename, line, p); 134 continue; 135 } 136 137 /* get control flag */ 138 for (p = q; isspace(*p); ++p) 139 /* nothing */; 140 for (q = p; *q != '\0' && !isspace(*q); ++q) 141 /* nothing */; 142 if (q == p || *q == '\0') 143 goto syntax_error; 144 *q++ = '\0'; 145 if (strcmp(p, "required") == 0) { 146 flag = PAM_REQUIRED; 147 } else if (strcmp(p, "requisite") == 0) { 148 flag = PAM_REQUISITE; 149 } else if (strcmp(p, "sufficient") == 0) { 150 flag = PAM_SUFFICIENT; 151 } else if (strcmp(p, "optional") == 0) { 152 flag = PAM_OPTIONAL; 153 } else if (strcmp(p, "binding") == 0) { 154 flag = PAM_BINDING; 155 } else { 156 openpam_log(PAM_LOG_ERROR, 157 "%s: invalid control flag on line %d: '%s'", 158 filename, line, p); 159 continue; 160 } 161 162 /* get module name */ 163 for (p = q; isspace(*p); ++p) 164 /* nothing */; 165 for (q = p; *q != '\0' && !isspace(*q); ++q) 166 /* nothing */; 167 if (q == p) 168 goto syntax_error; 169 170 /* get options */ 171 for (optc = 0; *q != '\0' && optc < MAX_OPTIONS; ++optc) { 172 *q++ = '\0'; 173 while (isspace(*q)) 174 ++q; 175 optv[optc] = q; 176 while (*q != '\0' && !isspace(*q)) 177 ++q; 178 } 179 optv[optc] = NULL; 180 if (*q != '\0') { 181 *q = '\0'; 182 openpam_log(PAM_LOG_ERROR, 183 "%s: too many options on line %d", 184 filename, line); 185 } 186 187 /* 188 * Finally, add the module at the end of the 189 * appropriate chain and bump the counter. 190 */ 191 r = openpam_add_module(policy, chain, flag, p, optc, optv); 192 if (r != PAM_SUCCESS) 193 return (-r); 194 ++n; 195 continue; 196 syntax_error: 197 openpam_log(PAM_LOG_ERROR, "%s: syntax error on line %d", 198 filename, line); 199 openpam_log(PAM_LOG_DEBUG, "%s: line %d: [%s]", 200 filename, line, q); 201 openpam_log(PAM_LOG_ERROR, "%s: ignoring line %d", 202 filename, line); 203 } 204 205 if (ferror(f)) 206 openpam_log(PAM_LOG_ERROR, "%s: %m", filename); 207 208 fclose(f); 209 return (n); 210} 211 212static const char *openpam_policy_path[] = { 213 "/etc/pam.d/", 214 "/etc/pam.conf", 215 "/usr/local/etc/pam.d/", 216 "/usr/local/etc/pam.conf", 217 NULL 218}; 219 220static int 221openpam_load_policy(pam_chain_t *policy[], 222 const char *service) 223{ 224 const char **path; 225 char *filename; 226 size_t len; 227 int r; 228 229 for (path = openpam_policy_path; *path != NULL; ++path) { 230 len = strlen(*path); 231 if ((*path)[len - 1] == '/') { 232 filename = malloc(len + strlen(service) + 1); 233 if (filename == NULL) { 234 openpam_log(PAM_LOG_ERROR, "malloc(): %m"); 235 return (-PAM_BUF_ERR); 236 } 237 strcpy(filename, *path); 238 strcat(filename, service); 239 r = openpam_read_policy_file(policy, 240 service, filename, PAM_D_STYLE); 241 free(filename); 242 } else { 243 r = openpam_read_policy_file(policy, 244 service, *path, PAM_CONF_STYLE); 245 } 246 if (r != 0) 247 return (r); 248 } 249 250 return (0); 251} 252 253/* 254 * OpenPAM internal 255 * 256 * Configure a service 257 */ 258 259int 260openpam_configure(pam_handle_t *pamh, 261 const char *service) 262{ 263 pam_chain_t *other[PAM_NUM_CHAINS] = { 0 }; 264 int i, n, r; 265 266 /* try own configuration first */ 267 r = openpam_load_policy(pamh->chains, service); 268 if (r < 0) 269 return (-r); 270 for (i = n = 0; i < PAM_NUM_CHAINS; ++i) { 271 if (pamh->chains[i] != NULL) 272 ++n; 273 } 274 if (n == PAM_NUM_CHAINS) 275 return (PAM_SUCCESS); 276 277 /* fill in the blanks with "other" */ 278 openpam_load_policy(other, PAM_OTHER); 279 if (r < 0) 280 return (-r); 281 for (i = n = 0; i < PAM_NUM_CHAINS; ++i) { 282 if (pamh->chains[i] == NULL) { 283 pamh->chains[i] = other[i]; 284 other[i] = NULL; 285 } 286 if (pamh->chains[i] != NULL) 287 ++n; 288 } 289 openpam_clear_chains(other); 290 return (n > 0 ? PAM_SUCCESS : PAM_SYSTEM_ERR); 291} 292 293/* 294 * NODOC 295 * 296 * Error codes: 297 * PAM_SYSTEM_ERR 298 * PAM_BUF_ERR 299 */ 300