int _pam_krb5_sly_maybe_refresh(pam_handle_t *pamh, int flags, int argc, PAM_KRB5_MAYBE_CONST char **argv) { PAM_KRB5_MAYBE_CONST char *user; krb5_context ctx; struct _pam_krb5_options *options; struct _pam_krb5_user_info *userinfo; struct _pam_krb5_stash *stash; struct stat st; int i, retval, stored; uid_t uid; gid_t gid; const char *v5ccname, *v5filename, *v4tktfile; #ifdef TKT_ROOT char v4tktfilebuf[PATH_MAX]; #endif /* Inexpensive checks. */ switch (_pam_krb5_sly_looks_unsafe()) { case 0: /* nothing: everything's okay */ break; case 1: warn("won't refresh credentials while running under sudo"); return PAM_SERVICE_ERR; break; case 2: warn("won't refresh credentials while running setuid"); return PAM_SERVICE_ERR; break; case 3: warn("won't refresh credentials while running setgid"); return PAM_SERVICE_ERR; break; default: warn("not safe to refresh credentials"); return PAM_SERVICE_ERR; break; } /* Initialize Kerberos. */ if (_pam_krb5_init_ctx(&ctx, argc, argv) != 0) { warn("error initializing Kerberos"); return PAM_SERVICE_ERR; } /* Get the user's name. */ i = pam_get_user(pamh, &user, NULL); if ((i != PAM_SUCCESS) || (user == NULL)) { warn("could not identify user name"); krb5_free_context(ctx); return i; } /* Read our options. */ options = _pam_krb5_options_init(pamh, argc, argv, ctx); if (options == NULL) { warn("error parsing options (shouldn't happen)"); krb5_free_context(ctx); return PAM_SERVICE_ERR; } if (options->debug) { debug("called to update credentials for '%s'", user); } /* Get information about the user and the user's principal name. */ userinfo = _pam_krb5_user_info_init(ctx, user, options); if (userinfo == NULL) { if (options->ignore_unknown_principals) { retval = PAM_IGNORE; } else { warn("error getting information about '%s' " "(shouldn't happen)", user); retval = PAM_USER_UNKNOWN; } _pam_krb5_options_free(pamh, ctx, options); krb5_free_context(ctx); return retval; } if ((options->user_check) && (options->minimum_uid != (uid_t)-1) && (userinfo->uid < options->minimum_uid)) { if (options->debug) { debug("ignoring '%s' -- uid below minimum", user); } _pam_krb5_user_info_free(ctx, userinfo); _pam_krb5_options_free(pamh, ctx, options); krb5_free_context(ctx); return PAM_IGNORE; } /* Get the stash for this user. */ stash = _pam_krb5_stash_get(pamh, user, userinfo, options); if (stash == NULL) { warn("error retrieving stash for '%s' (shouldn't happen)", user); _pam_krb5_user_info_free(ctx, userinfo); _pam_krb5_options_free(pamh, ctx, options); krb5_free_context(ctx); return PAM_SERVICE_ERR; } retval = PAM_SERVICE_ERR; /* Save credentials in the right places. */ v5ccname = krb5_cc_default_name(ctx); v5filename = NULL; if (v5ccname == NULL) { /* This should never happen, but all we can do is tell libpam * to ignore us. We have nothing to do. */ if (options->debug) { debug("ignoring '%s' -- no default ccache name", user); } retval = PAM_IGNORE; } else { if (strncmp(v5ccname, "FILE:", 5) == 0) { v5filename = v5ccname + 5; if (options->debug) { debug("ccache is a file named '%s'", v5filename); } } else { if (options->debug) { debug("ccache '%s' is not a file", v5ccname); } } } stored = 0; uid = options->user_check ? userinfo->uid : getuid(); gid = options->user_check ? userinfo->gid : getgid(); if (v5_creds_check_initialized(ctx, &stash->v5creds) == 0) { if (v5filename != NULL) { /* Check the permissions on the ccache file. */ if ((access(v5filename, R_OK | W_OK) == 0) && (lstat(v5filename, &st) == 0)) { if (S_ISREG(st.st_mode) && ((st.st_mode & S_IRWXG) == 0) && ((st.st_mode & S_IRWXO) == 0) && (st.st_uid == uid) && (st.st_gid == gid)) { if (options->debug) { debug("updating ccache '%s' " "for '%s'", v5ccname, user); } retval = sly_v5(ctx, v5ccname, userinfo, stash); stored = (retval == PAM_SUCCESS); } else { if (options->debug) { debug("not updating '%s'", v5ccname); } retval = PAM_SUCCESS; } } else { if (errno == ENOENT) { /* We have nothing to do. */ retval = PAM_SUCCESS; } } } else { if (v5ccname != NULL) { /* Go ahead and update the current not-a-file * ccache. */ if (options->debug) { debug("updating ccache '%s' for '%s'", v5ccname, user); } retval = sly_v5(ctx, v5ccname, userinfo, stash); stored = (retval == PAM_SUCCESS); } } } else { if (options->debug) { debug("no credentials available to store in '%s'", v5ccname); } retval = PAM_SUCCESS; } v4tktfile = getenv("KRBTKFILE"); #ifdef TKT_ROOT if ((v4tktfile == NULL) && (options->user_check)) { snprintf(v4tktfilebuf, sizeof(v4tktfilebuf), "%s%ld", TKT_ROOT, (long) uid); v4tktfile = v4tktfilebuf; } #endif if ((stash->v4present) && (v4tktfile != NULL)) { if (access(v4tktfile, R_OK | W_OK) == 0) { if (lstat(v4tktfile, &st) == 0) { if (S_ISREG(st.st_mode) && ((st.st_mode & S_IRWXG) == 0) && ((st.st_mode & S_IRWXO) == 0) && (st.st_uid == uid) && (st.st_gid == gid)) { if (options->debug) { debug("updating ticket file " "'%s' for '%s'", v4tktfile, user); } sly_v4(ctx, v4tktfile, userinfo, stash); stored = 1; } else { if (options->debug) { debug("not updating '%s'", v4tktfile); } } } else { if (errno == ENOENT) { /* We have nothing to do. */ if (options->debug) { debug("no preexisting ticket " "file found"); } retval = PAM_SUCCESS; } } } else { /* Touch nothing. */ if (options->debug) { debug("unable to access preexisting ticket " "file"); } retval = PAM_SUCCESS; } } if (stored && !options->ignore_afs) { tokens_obtain(ctx, stash, options, userinfo, 0); } if (options->debug) { debug("_pam_krb5_sly_refresh returning %d (%s)", retval, pam_strerror(pamh, retval)); } _pam_krb5_user_info_free(ctx, userinfo); _pam_krb5_options_free(pamh, ctx, options); krb5_free_context(ctx); return retval; }
/* Determine in which realm a cell exists. We do this by obtaining the address * of the fileserver which holds /afs/cellname (assuming that the root.cell * volume from the cell is mounted there), converting the address to a host * name, and then asking libkrb5 to tell us to which realm the host belongs. */ static int minikafs_realm_of_cell_with_ctx(krb5_context ctx, struct _pam_krb5_options *options, const char *cell, char *realm, size_t length) { struct minikafs_ioblock iob; struct sockaddr_in sin; in_addr_t *address; krb5_context use_ctx; char *path, host[HOSTNAME_SIZE], **realms; int i, n_addresses, ret; if (cell) { path = malloc(strlen(cell) + 6); } else { path = malloc(5); } if (path == NULL) { return -1; } if (cell) { sprintf(path, "/afs/%s", cell); } else { sprintf(path, "/afs"); } n_addresses = 16; do { /* allocate the output buffer for the address [list] */ address = malloc(n_addresses * sizeof(address[0])); if (address == NULL) { ret = -1; break; } memset(address, 0, n_addresses * sizeof(address[0])); memset(&iob, 0, sizeof(iob)); iob.in = path; iob.insize = strlen(path) + 1; iob.out = (char*) &address[0]; iob.outsize = n_addresses * sizeof(address[0]); ret = minikafs_pioctl(path, minikafs_pioctl_whereis, &iob); /* if we failed, free the address [list], and if the error was * E2BIG, increase the size we'll use next time, up to a * hard-coded limit */ if (ret != 0) { if (options->debug) { debug("error during whereis pioctl: %s", strerror(errno)); } free(address); address = NULL; if (errno == E2BIG) { if (n_addresses > 256) { if (options->debug) { debug("giving up"); } break; } if (options->debug) { debug("retrying"); } n_addresses *= 2; } } } while ((ret != 0) && (errno == E2BIG)); if (ret != 0) { if (options->debug) { debug("got error %d (%s) determining file server for " "\"%s\"", errno, v5_error_message(errno), path); } free(path); return ret; } free(path); sin.sin_family = AF_INET; if (options->debug) { for (i = 0; (i < n_addresses) && (address[i] != 0); i++) { debug("file server for \"/afs/%s\" is %u.%u.%u.%u", cell, (address[i] >> 0) & 0xff, (address[i] >> 8) & 0xff, (address[i] >> 16) & 0xff, (address[i] >> 24) & 0xff); } } if (ctx == NULL) { if (_pam_krb5_init_ctx(&use_ctx, 0, NULL) != 0) { free(address); return -1; } } else { use_ctx = ctx; } for (i = 0; (i < n_addresses) && (address[i] != 0); i++) { memcpy(&sin.sin_addr, &address[i], sizeof(address[i])); if (getnameinfo((const struct sockaddr*) &sin, sizeof(sin), host, sizeof(host), NULL, 0, NI_NAMEREQD) == 0) { if (options->debug) { debug("file server %d.%d.%d.%d has name %s", (address[i] >> 0) & 0xff, (address[i] >> 8) & 0xff, (address[i] >> 16) & 0xff, (address[i] >> 24) & 0xff, host); } if (krb5_get_host_realm(use_ctx, host, &realms) == 0) { strncpy(realm, realms[0], length - 1); realm[length - 1] = '\0'; krb5_free_host_realm(use_ctx, realms); if (options->debug) { debug("%s is in realm %s", host, realm); } i = 0; break; } } else { if (options->debug) {