openpam_configure.c revision 99158
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#5 $ 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 NULL 217}; 218 219static int 220openpam_load_policy(pam_chain_t *policy[], 221 const char *service) 222{ 223 const char **path; 224 char *filename; 225 size_t len; 226 int r; 227 228 for (path = openpam_policy_path; *path != NULL; ++path) { 229 len = strlen(*path); 230 if ((*path)[len - 1] == '/') { 231 filename = malloc(len + strlen(service) + 1); 232 if (filename == NULL) { 233 openpam_log(PAM_LOG_ERROR, "malloc(): %m"); 234 return (-PAM_BUF_ERR); 235 } 236 strcpy(filename, *path); 237 strcat(filename, service); 238 r = openpam_read_policy_file(policy, 239 service, filename, PAM_D_STYLE); 240 free(filename); 241 } else { 242 r = openpam_read_policy_file(policy, 243 service, *path, PAM_CONF_STYLE); 244 } 245 if (r != 0) 246 return (r); 247 } 248 249 return (0); 250} 251 252/* 253 * OpenPAM internal 254 * 255 * Configure a service 256 */ 257 258int 259openpam_configure(pam_handle_t *pamh, 260 const char *service) 261{ 262 pam_chain_t *other[PAM_NUM_CHAINS] = { 0 }; 263 int i, n, r; 264 265 /* try own configuration first */ 266 r = openpam_load_policy(pamh->chains, service); 267 if (r < 0) 268 return (-r); 269 for (i = n = 0; i < PAM_NUM_CHAINS; ++i) { 270 if (pamh->chains[i] != NULL) 271 ++n; 272 } 273 if (n == PAM_NUM_CHAINS) 274 return (PAM_SUCCESS); 275 276 /* fill in the blanks with "other" */ 277 openpam_load_policy(other, PAM_OTHER); 278 if (r < 0) 279 return (-r); 280 for (i = n = 0; i < PAM_NUM_CHAINS; ++i) { 281 if (pamh->chains[i] == NULL) { 282 pamh->chains[i] = other[i]; 283 other[i] = NULL; 284 } 285 if (pamh->chains[i] != NULL) 286 ++n; 287 } 288 openpam_clear_chains(other); 289 return (n > 0 ? PAM_SUCCESS : PAM_SYSTEM_ERR); 290} 291 292/* 293 * NODOC 294 * 295 * Error codes: 296 * PAM_SYSTEM_ERR 297 * PAM_BUF_ERR 298 */ 299