1 /* 2 * Copyright 2003,2004,2005,2006,2007,2008,2009,2011,2014 Red Hat, Inc. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, and the entire permission notice in its entirety, 9 * including the disclaimer of warranties. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote 14 * products derived from this software without specific prior 15 * written permission. 16 * 17 * ALTERNATIVELY, this product may be distributed under the terms of the 18 * GNU Lesser General Public License, in which case the provisions of the 19 * LGPL are required INSTEAD OF the above restrictions. 20 * 21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 24 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 27 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include "../config.h" 34 35 #ifdef HAVE_SECURITY_PAM_APPL_H 36 #include <security/pam_appl.h> 37 #endif 38 39 #ifdef HAVE_SECURITY_PAM_MODULES_H 40 #define PAM_SM_SESSION 41 #include <security/pam_modules.h> 42 #endif 43 44 #include <limits.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 #include KRB5_H 51 52 #include "init.h" 53 #include "log.h" 54 #include "options.h" 55 #include "prompter.h" 56 #include "session.h" 57 #include "shmem.h" 58 #include "stash.h" 59 #include "tokens.h" 60 #include "userinfo.h" 61 #include "v5.h" 62 #include "xstr.h" 63 64 int 65 _pam_krb5_open_session(pam_handle_t *pamh, int flags, 66 int argc, PAM_KRB5_MAYBE_CONST char **argv, 67 const char *caller, 68 enum _pam_krb5_session_caller caller_type) 69 { 70 PAM_KRB5_MAYBE_CONST char *user; 71 char envstr[PATH_MAX + 20], *segname; 72 const char *ccname; 73 krb5_context ctx; 74 struct _pam_krb5_options *options; 75 struct _pam_krb5_user_info *userinfo; 76 struct _pam_krb5_stash *stash; 77 int i, retval; 78 79 /* Initialize Kerberos. */ 80 if (_pam_krb5_init_ctx(&ctx, argc, argv) != 0) { 81 warn("error initializing Kerberos"); 82 return PAM_SERVICE_ERR; 83 } 84 85 /* Get the user's name. */ 86 i = pam_get_user(pamh, &user, NULL); 87 if ((i != PAM_SUCCESS) || (user == NULL)) { 88 warn("could not identify user name"); 89 _pam_krb5_free_ctx(ctx); 90 return i; 91 } 92 93 /* Read our options. */ 94 options = _pam_krb5_options_init(pamh, argc, argv, ctx, 95 _pam_krb5_option_role_general); 96 if (options == NULL) { 97 warn("error parsing options (shouldn't happen)"); 98 _pam_krb5_free_ctx(ctx); 99 return PAM_SERVICE_ERR; 100 } 101 102 /* If we're in a no-cred-session situation, return. */ 103 if ((!options->cred_session) && 104 (caller_type == _pam_krb5_session_caller_setcred)) { 105 _pam_krb5_options_free(pamh, ctx, options); 106 _pam_krb5_free_ctx(ctx); 107 return PAM_SUCCESS; 108 } 109 110 /* Get information about the user and the user's principal name. */ 111 userinfo = _pam_krb5_user_info_init(ctx, user, options); 112 if (userinfo == NULL) { 113 if (options->debug) { 114 debug("no user info for '%s'", user); 115 } 116 if (options->ignore_unknown_principals) { 117 retval = PAM_IGNORE; 118 } else { 119 retval = PAM_USER_UNKNOWN; 120 } 121 if (options->debug) { 122 debug("%s returning %d (%s)", caller, 123 retval, 124 pam_strerror(pamh, retval)); 125 } 126 _pam_krb5_options_free(pamh, ctx, options); 127 _pam_krb5_free_ctx(ctx); 128 return retval; 129 } 130 if ((options->user_check) && 131 (options->minimum_uid != (uid_t)-1) && 132 (userinfo->uid < options->minimum_uid)) { 133 if (options->debug) { 134 debug("ignoring '%s' -- uid below minimum = %lu", user, 135 (unsigned long) options->minimum_uid); 136 } 137 _pam_krb5_user_info_free(ctx, userinfo); 138 if (options->debug) { 139 debug("%s returning %d (%s)", caller, PAM_IGNORE, 140 pam_strerror(pamh, PAM_IGNORE)); 141 } 142 _pam_krb5_options_free(pamh, ctx, options); 143 _pam_krb5_free_ctx(ctx); 144 return PAM_IGNORE; 145 } 146 147 /* Get the stash for this user. */ 148 stash = _pam_krb5_stash_get(pamh, user, userinfo, options); 149 if (stash == NULL) { 150 warn("no stash for '%s' (shouldn't happen)", user); 151 _pam_krb5_user_info_free(ctx, userinfo); 152 if (options->debug) { 153 debug("%s returning %d (%s)", caller, 154 PAM_SERVICE_ERR, 155 pam_strerror(pamh, PAM_SERVICE_ERR)); 156 } 157 _pam_krb5_options_free(pamh, ctx, options); 158 _pam_krb5_free_ctx(ctx); 159 return PAM_SERVICE_ERR; 160 } 161 162 /* We don't need the shared memory segments any more, so we can get rid 163 * of them now. (Depending on the application, we may not get a chance 164 * to do it later.) */ 165 if (options->use_shmem) { 166 if ((stash->v5shm != -1) && (stash->v5shm_owner != -1)) { 167 if (options->debug) { 168 debug("removing shared memory segment %d" 169 " creator pid %ld", 170 stash->v5shm, (long) stash->v5shm_owner); 171 } 172 _pam_krb5_shm_remove(stash->v5shm_owner, stash->v5shm, 173 options->debug); 174 stash->v5shm = -1; 175 _pam_krb5_stash_shm_var_name(options, user, &segname); 176 if (segname != NULL) { 177 pam_putenv(pamh, segname); 178 free(segname); 179 } 180 } 181 } 182 183 /* If we don't have any credentials, then we're done. */ 184 if ((stash->v5attempted == 0) || (stash->v5result != 0)) { 185 if (options->debug) { 186 debug("no creds for user '%s', " 187 "skipping session setup", user); 188 } 189 _pam_krb5_user_info_free(ctx, userinfo); 190 if (options->debug) { 191 debug("%s returning %d (%s)", caller, PAM_SUCCESS, 192 pam_strerror(pamh, PAM_SUCCESS)); 193 } 194 _pam_krb5_options_free(pamh, ctx, options); 195 _pam_krb5_free_ctx(ctx); 196 return PAM_SUCCESS; 197 } 198 199 /* Obtain tokens, if necessary. */ 200 if ((i == PAM_SUCCESS) && 201 (options->ignore_afs == 0) && 202 tokens_useful()) { 203 tokens_obtain(ctx, stash, options, userinfo, 1); 204 } 205 206 /* Create the user's credential cache, but only if we didn't pick them 207 * up from our calling process. */ 208 if (!stash->v5external) { 209 if (options->debug) { 210 #ifdef HAVE_LONG_LONG 211 debug("creating ccache for '%s', uid=%llu, gid=%llu", 212 user, 213 options->user_check ? 214 (unsigned long long) userinfo->uid : 215 (unsigned long long) getuid(), 216 options->user_check ? 217 (unsigned long long) userinfo->gid : 218 (unsigned long long) getgid()); 219 #else 220 debug("creating ccache for '%s', uid=%lu, gid=%lu", 221 user, 222 options->user_check ? 223 (unsigned long) userinfo->uid : 224 (unsigned long) getuid(), 225 options->user_check ? 226 (unsigned long) userinfo->gid : 227 (unsigned long) getgid()); 228 #endif 229 } 230 i = v5_save_for_user(ctx, stash, user, userinfo, 231 options, &ccname); 232 if ((i == PAM_SUCCESS) && (strlen(ccname) > 0)) { 233 sprintf(envstr, "KRB5CCNAME=%s", ccname); 234 pam_putenv(pamh, envstr); 235 stash->v5setenv = 1; 236 } else { 237 if (options->debug) { 238 debug("failed to create ccache for '%s'", user); 239 } 240 } 241 } 242 243 /* If we didn't create ccache files because we couldn't, just 244 * pretend everything's fine. */ 245 if ((i != PAM_SUCCESS) && 246 (v5_ccache_has_tgt(ctx, stash->v5ccache, 247 options->realm, NULL) != 0)) { 248 i = PAM_SUCCESS; 249 } 250 251 /* Clean up. */ 252 if (options->debug) { 253 debug("%s returning %d (%s)", caller, i, 254 pam_strerror(pamh, i)); 255 } 256 _pam_krb5_options_free(pamh, ctx, options); 257 _pam_krb5_user_info_free(ctx, userinfo); 258 259 260 _pam_krb5_free_ctx(ctx); 261 return i; 262 } 263 264 int 265 _pam_krb5_close_session(pam_handle_t *pamh, int flags, 266 int argc, PAM_KRB5_MAYBE_CONST char **argv, 267 const char *caller, 268 enum _pam_krb5_session_caller caller_type) 269 { 270 PAM_KRB5_MAYBE_CONST char *user; 271 krb5_context ctx; 272 struct _pam_krb5_options *options; 273 struct _pam_krb5_user_info *userinfo; 274 struct _pam_krb5_stash *stash; 275 int i, retval; 276 277 /* Initialize Kerberos. */ 278 if (_pam_krb5_init_ctx(&ctx, argc, argv) != 0) { 279 warn("error initializing Kerberos"); 280 return PAM_SERVICE_ERR; 281 } 282 283 /* Get the user's name. */ 284 i = pam_get_user(pamh, &user, NULL); 285 if (i != PAM_SUCCESS) { 286 warn("could not determine user name"); 287 _pam_krb5_free_ctx(ctx); 288 return i; 289 } 290 291 /* Read our options. */ 292 options = _pam_krb5_options_init(pamh, argc, argv, ctx, 293 _pam_krb5_option_role_general); 294 if (options == NULL) { 295 _pam_krb5_free_ctx(ctx); 296 return PAM_SERVICE_ERR; 297 } 298 299 /* If we're in a no-cred-session situation, return. */ 300 if ((!options->cred_session) && 301 (caller_type == _pam_krb5_session_caller_setcred)) { 302 _pam_krb5_options_free(pamh, ctx, options); 303 _pam_krb5_free_ctx(ctx); 304 return PAM_SUCCESS; 305 } 306 307 /* Get information about the user and the user's principal name. */ 308 userinfo = _pam_krb5_user_info_init(ctx, user, options); 309 if (userinfo == NULL) { 310 if (options->ignore_unknown_principals) { 311 retval = PAM_IGNORE; 312 } else { 313 warn("no user info for %s (shouldn't happen)", user); 314 retval = PAM_USER_UNKNOWN; 315 } 316 if (options->debug) { 317 debug("%s returning %d (%s)", caller, 318 retval, 319 pam_strerror(pamh, retval)); 320 } 321 _pam_krb5_options_free(pamh, ctx, options); 322 _pam_krb5_free_ctx(ctx); 323 return retval; 324 } 325 326 /* Check the minimum UID argument. */ 327 if ((options->user_check) && 328 (options->minimum_uid != (uid_t)-1) && 329 (userinfo->uid < options->minimum_uid)) { 330 if (options->debug) { 331 debug("ignoring '%s' -- uid below minimum", user); 332 } 333 _pam_krb5_user_info_free(ctx, userinfo); 334 if (options->debug) { 335 debug("%s returning %d (%s)", caller, PAM_IGNORE, 336 pam_strerror(pamh, PAM_IGNORE)); 337 } 338 _pam_krb5_options_free(pamh, ctx, options); 339 _pam_krb5_free_ctx(ctx); 340 return PAM_IGNORE; 341 } 342 343 /* Get the stash for this user. */ 344 stash = _pam_krb5_stash_get(pamh, user, userinfo, options); 345 if (stash == NULL) { 346 warn("no stash for user %s (shouldn't happen)", user); 347 _pam_krb5_user_info_free(ctx, userinfo); 348 if (options->debug) { 349 debug("%s returning %d (%s)", caller, 350 PAM_SERVICE_ERR, 351 pam_strerror(pamh, PAM_SERVICE_ERR)); 352 } 353 _pam_krb5_options_free(pamh, ctx, options); 354 _pam_krb5_free_ctx(ctx); 355 return PAM_SERVICE_ERR; 356 } 357 358 /* If we didn't obtain any credentials, then we're done. */ 359 if ((stash->v5attempted == 0) || (stash->v5result != 0)) { 360 if (options->debug) { 361 debug("no creds for user '%s', " 362 "skipping session cleanup", 363 user); 364 } 365 _pam_krb5_user_info_free(ctx, userinfo); 366 if (options->debug) { 367 debug("%s returning %d (%s)", caller, 368 PAM_SUCCESS, 369 pam_strerror(pamh, PAM_SUCCESS)); 370 } 371 _pam_krb5_options_free(pamh, ctx, options); 372 _pam_krb5_free_ctx(ctx); 373 return PAM_SUCCESS; 374 } 375 376 if (options->ignore_afs == 0) { 377 tokens_release(stash, options); 378 } 379 380 if (!stash->v5external) { 381 if (stash->v5ccnames != NULL) { 382 v5_destroy(ctx, stash, options); 383 if (stash->v5setenv) { 384 pam_putenv(pamh, "KRB5CCNAME"); 385 stash->v5setenv = 0; 386 } 387 } 388 } else { 389 if (options->debug) { 390 debug("leaving external ccache for '%s'", user); 391 } 392 } 393 394 _pam_krb5_user_info_free(ctx, userinfo); 395 if (options->debug) { 396 debug("%s returning %d (%s)", caller, 397 PAM_SUCCESS, 398 pam_strerror(pamh, PAM_SUCCESS)); 399 } 400 _pam_krb5_options_free(pamh, ctx, options); 401 _pam_krb5_free_ctx(ctx); 402 return PAM_SUCCESS; 403 } 404 405 int 406 pam_sm_open_session(pam_handle_t *pamh, int flags, 407 int argc, PAM_KRB5_MAYBE_CONST char **argv) 408 { 409 return _pam_krb5_open_session(pamh, flags, argc, argv, 410 "pam_sm_open_session", 411 _pam_krb5_session_caller_session); 412 } 413 414 int 415 pam_sm_close_session(pam_handle_t *pamh, int flags, 416 int argc, PAM_KRB5_MAYBE_CONST char **argv) 417 { 418 return _pam_krb5_close_session(pamh, flags, argc, argv, 419 "pam_sm_close_session", 420 _pam_krb5_session_caller_session); 421 } 422