Exemple #1
0
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) {