2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/krb5/os/kuserok.c
2N/A *
2N/A * Copyright 1990,1993,2007 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A/*
2N/A * krb5_kuserok()
2N/A */
2N/A
2N/A#include "k5-int.h"
2N/A#if !defined(_WIN32) /* Not yet for Windows */
2N/A#include <stdio.h>
2N/A/* Solaris Kerberos */
2N/A#include <string.h>
2N/A#include <stdlib.h>
2N/A#include <pwd.h>
2N/A#include <libintl.h>
2N/A#include <gssapi/gssapi.h>
2N/A#include <gssapi/gssapi_ext.h>
2N/A#include <gssapi_krb5.h>
2N/A#include <gssapiP_krb5.h>
2N/A#include <syslog.h>
2N/A
2N/A#if defined(_AIX) && defined(_IBMR2)
2N/A#include <sys/access.h>
2N/A/* xlc has a bug with "const" */
2N/A#define getpwnam(user) getpwnam((char *)user)
2N/A#endif
2N/A
2N/A#define MAX_USERNAME 65
2N/A#define CACHE_FILENAME_LEN 35
2N/A
2N/A#if defined(__APPLE__) && defined(__MACH__)
2N/A#include <hfs/hfs_mount.h> /* XXX */
2N/A#define FILE_OWNER_OK(UID) ((UID) == 0 || (UID) == UNKNOWNUID)
2N/A#else
2N/A#define FILE_OWNER_OK(UID) ((UID) == 0)
2N/A#endif
2N/A
2N/A/* Solaris Kerberos */
2N/Aextern void
2N/Agsscred_set_options();
2N/A
2N/Aextern OM_uint32
2N/Agsscred_name_to_unix_cred_ext();
2N/A
2N/Aextern int
2N/Asafechown(const char *src, uid_t uid, gid_t gid, int mode);
2N/A
2N/Aextern const char *error_message(long);
2N/A
2N/Akrb5_data tgtname = {
2N/A 0,
2N/A KRB5_TGS_NAME_SIZE,
2N/A KRB5_TGS_NAME
2N/A};
2N/A
2N/A/* Solaris Kerberos */
2N/Astatic krb5_error_code
2N/Akrb5_move_ccache(krb5_context kcontext, krb5_principal client,
2N/A struct passwd *pwd)
2N/A{
2N/A char *name = 0;
2N/A static char ccache_name_buf[CACHE_FILENAME_LEN];
2N/A krb5_ccache ccache = NULL;
2N/A krb5_error_code retval;
2N/A
2N/A name = getenv(KRB5_ENV_CCNAME);
2N/A if (name == 0)
2N/A /*
2N/A * This means that there was no forwarding
2N/A * of creds
2N/A */
2N/A return (0);
2N/A else {
2N/A /*
2N/A * creds have been forwarded and stored in
2N/A * KRB5_ENV_CCNAME and now we need to store it
2N/A * under uid
2N/A */
2N/A
2N/A krb5_creds mcreds, save_v5creds;
2N/A
2N/A memset(&mcreds, 0, sizeof (mcreds));
2N/A memset(&save_v5creds, 0, sizeof (save_v5creds));
2N/A
2N/A mcreds.client = client;
2N/A retval = krb5_build_principal_ext(kcontext, &mcreds.server,
2N/A krb5_princ_realm(kcontext, client)->length,
2N/A krb5_princ_realm(kcontext, client)->data,
2N/A tgtname.length, tgtname.data,
2N/A krb5_princ_realm(kcontext, client)->length,
2N/A krb5_princ_realm(kcontext, client)->data,
2N/A 0);
2N/A if (retval) {
2N/A syslog(LOG_ERR,
2N/A gettext("KRB5: %s while creating"
2N/A "V5 krbtgt principal "),
2N/A error_message(retval));
2N/A return (retval);
2N/A }
2N/A
2N/A mcreds.ticket_flags = 0;
2N/A retval = krb5_cc_default(kcontext, &ccache);
2N/A if (retval) {
2N/A syslog(LOG_ERR,
2N/A gettext("KRB5: %s while getting "
2N/A "default cache "),
2N/A error_message(retval));
2N/A return (retval);
2N/A }
2N/A
2N/A retval = krb5_cc_retrieve_cred(kcontext, ccache,
2N/A 0,
2N/A &mcreds, &save_v5creds);
2N/A if (retval) {
2N/A syslog(LOG_ERR,
2N/A gettext("KRB5: %s while retrieving "
2N/A "cerdentials "),
2N/A error_message(retval));
2N/A return (retval);
2N/A }
2N/A /*
2N/A * reset the env variable and recreate the
2N/A * cache using the default cache name
2N/A */
2N/A retval = krb5_cc_destroy(kcontext, ccache);
2N/A if (retval) {
2N/A syslog(LOG_ERR,
2N/A gettext("KRB5: %s while destroying cache "),
2N/A error_message(retval));
2N/A return (retval);
2N/A }
2N/A krb5_unsetenv(KRB5_ENV_CCNAME);
2N/A snprintf(ccache_name_buf,
2N/A CACHE_FILENAME_LEN,
2N/A "FILE:/tmp/krb5cc_%d", pwd->pw_uid);
2N/A krb5_setenv(KRB5_ENV_CCNAME, ccache_name_buf, 1);
2N/A retval = krb5_cc_resolve(kcontext, ccache_name_buf, &ccache);
2N/A if (retval) {
2N/A syslog(LOG_ERR,
2N/A gettext("KRB5: %s while resolving cache "),
2N/A error_message(retval));
2N/A return (retval);
2N/A }
2N/A retval = krb5_cc_initialize(kcontext, ccache, client);
2N/A if (retval) {
2N/A syslog(LOG_ERR,
2N/A gettext("KRB5: %s while initializing cache "),
2N/A error_message(retval));
2N/A return (retval);
2N/A }
2N/A retval = krb5_cc_store_cred(kcontext, ccache, &save_v5creds);
2N/A if (retval) {
2N/A syslog(LOG_ERR,
2N/A gettext("KRB5: %s while storing creds "),
2N/A error_message(retval));
2N/A return (retval);
2N/A }
2N/A snprintf(ccache_name_buf,
2N/A CACHE_FILENAME_LEN,
2N/A "/tmp/krb5cc_%d", pwd->pw_uid);
2N/A if (safechown(ccache_name_buf, pwd->pw_uid,
2N/A pwd->pw_gid, -1) == -1) {
2N/A syslog(LOG_ERR,
2N/A gettext("KRB5: Can not change "
2N/A "ownership of cache file, "
2N/A "possible security breach\n"));
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Solaris Kerberos:
2N/A * krb5_gsscred: Given a kerberos principal try to find the corresponding
2N/A * local uid via the gss cred table. Return TRUE if the uid was found in the
2N/A * cred table, otherwise return FALSE.
2N/A */
2N/Astatic krb5_boolean
2N/Akrb5_gsscred(krb5_principal principal, uid_t *uid)
2N/A{
2N/A OM_uint32 minor, major;
2N/A gss_name_t name;
2N/A gss_buffer_desc name_buf;
2N/A
2N/A name_buf.value = &principal;
2N/A name_buf.length = sizeof (principal);
2N/A
2N/A /*
2N/A * Convert the kerb principal in to a gss name
2N/A */
2N/A major = gss_import_name(&minor, &name_buf,
2N/A (gss_OID)gss_nt_krb5_principal, &name);
2N/A
2N/A if (major != GSS_S_COMPLETE)
2N/A return (FALSE);
2N/A
2N/A gsscred_set_options();
2N/A
2N/A /*
2N/A * Get the uid mapping from the gsscred table.
2N/A * (but set flag to not call back into this mech as we do krb5
2N/A * auth_to_local name mapping from this module).
2N/A */
2N/A major = gsscred_name_to_unix_cred_ext(name, (gss_OID)gss_mech_krb5,
2N/A uid, 0, 0, 0, 0);
2N/A
2N/A (void) gss_release_name(&minor, &name);
2N/A
2N/A if (major != GSS_S_COMPLETE)
2N/A return (FALSE);
2N/A
2N/A return (TRUE);
2N/A}
2N/A
2N/A/*
2N/A * Given a Kerberos principal "principal", and a local username "luser",
2N/A * determine whether user is authorized to login according to the
2N/A * authorization file ("~luser/.k5login" by default). Returns TRUE
2N/A * if authorized, FALSE if not authorized.
2N/A *
2N/A * If there is no account for "luser" on the local machine, returns
2N/A * FALSE. If there is no authorization file, and the given Kerberos
2N/A * name "server" translates to the same name as "luser" (using
2N/A * krb5_aname_to_lname()), returns TRUE. Otherwise, if the authorization file
2N/A * can't be accessed, returns FALSE. Otherwise, the file is read for
2N/A * a matching principal name, instance, and realm. If one is found,
2N/A * returns TRUE, if none is found, returns FALSE.
2N/A *
2N/A * The file entries are in the format produced by krb5_unparse_name(),
2N/A * one entry per line.
2N/A *
2N/A */
2N/A
2N/Akrb5_boolean KRB5_CALLCONV
2N/Akrb5_kuserok(krb5_context context, krb5_principal principal, const char *luser)
2N/A{
2N/A struct stat sbuf;
2N/A struct passwd *pwd;
2N/A char pbuf[MAXPATHLEN];
2N/A krb5_boolean isok = FALSE;
2N/A FILE *fp;
2N/A char kuser[MAX_USERNAME];
2N/A char *princname;
2N/A char linebuf[BUFSIZ];
2N/A char *newline;
2N/A /* Solaris Kerberos */
2N/A uid_t uid;
2N/A int gobble;
2N/A char pwbuf[BUFSIZ];
2N/A struct passwd pwx;
2N/A int result;
2N/A
2N/A /* no account => no access */
2N/A if (k5_getpwnam_r(luser, &pwx, pwbuf, sizeof(pwbuf), &pwd) != 0)
2N/A return(FALSE);
2N/A result = snprintf(pbuf, sizeof(pbuf), "%s/.k5login", pwd->pw_dir);
2N/A if (SNPRINTF_OVERFLOW(result, sizeof(pbuf)))
2N/A return(FALSE);
2N/A
2N/A if (access(pbuf, F_OK)) { /* not accessible */
2N/A /*
2N/A * if he's trying to log in as himself, and there is no .k5login file,
2N/A * let him. First, have krb5 check it's rules. If no success search
2N/A * the gsscred table (the sequence here should be consistent with the
2N/A * uid mappings done for gssd). To find out, call
2N/A * krb5_aname_to_localname to convert the principal to a name
2N/A * which we can string compare.
2N/A */
2N/A if (!(krb5_aname_to_localname(context, principal,
2N/A sizeof(kuser), kuser))
2N/A && (strcmp(kuser, luser) == 0)) {
2N/A /* Solaris Kerberos */
2N/A if (krb5_move_ccache(context, principal, pwd))
2N/A return (FALSE);
2N/A return(TRUE);
2N/A }
2N/A /* Solaris Kerberos */
2N/A if (krb5_gsscred(principal, &uid)) {
2N/A#ifdef DEBUG
2N/A char *princname;
2N/A
2N/A (void)krb5_unparse_name(context, principal, &princname);
2N/A syslog(LOG_DEBUG, "gsscred mapped %s to %d expecting %d (%s)\n",
2N/A princname, uid, pwd->pw_uid, luser);
2N/A free(princname);
2N/A#endif
2N/A if (uid == pwd->pw_uid) {
2N/A if (krb5_move_ccache(context, principal, pwd))
2N/A return (FALSE);
2N/A return (TRUE);
2N/A }
2N/A }
2N/A
2N/A }
2N/A if (krb5_unparse_name(context, principal, &princname))
2N/A return(FALSE); /* no hope of matching */
2N/A
2N/A /* open ~/.k5login */
2N/A /* Solaris Kerberos > 256 file descriptor enhancement */
2N/A if ((fp = fopen(pbuf, "rF")) == NULL) {
2N/A free(princname);
2N/A return(FALSE);
2N/A }
2N/A set_cloexec_file(fp);
2N/A /*
2N/A * For security reasons, the .k5login file must be owned either by
2N/A * the user himself, or by root. Otherwise, don't grant access.
2N/A */
2N/A if (fstat(fileno(fp), &sbuf)) {
2N/A fclose(fp);
2N/A free(princname);
2N/A return(FALSE);
2N/A }
2N/A if (sbuf.st_uid != pwd->pw_uid && !FILE_OWNER_OK(sbuf.st_uid)) {
2N/A fclose(fp);
2N/A free(princname);
2N/A return(FALSE);
2N/A }
2N/A
2N/A /* check each line */
2N/A while (!isok && (fgets(linebuf, BUFSIZ, fp) != NULL)) {
2N/A /* null-terminate the input string */
2N/A linebuf[BUFSIZ-1] = '\0';
2N/A newline = NULL;
2N/A /* nuke the newline if it exists */
2N/A if ((newline = strchr(linebuf, '\n')))
2N/A *newline = '\0';
2N/A if (!strcmp(linebuf, princname)) {
2N/A isok = TRUE;
2N/A /* Solaris Kerberos */
2N/A if (krb5_move_ccache(context, principal, pwd))
2N/A return (FALSE);
2N/A continue;
2N/A }
2N/A /* clean up the rest of the line if necessary */
2N/A if (!newline)
2N/A while (((gobble = getc(fp)) != EOF) && gobble != '\n');
2N/A }
2N/A free(princname);
2N/A fclose(fp);
2N/A return(isok);
2N/A}
2N/A
2N/A/* Solaris Kerberos */
2N/AOM_uint32
2N/Akrb5_gss_userok(OM_uint32 *minor,
2N/A const gss_name_t pname,
2N/A const char *user,
2N/A int *user_ok)
2N/A{
2N/A krb5_context ctxt;
2N/A OM_uint32 kret;
2N/A
2N/A if (pname == NULL || user == NULL)
2N/A return (GSS_S_CALL_INACCESSIBLE_READ);
2N/A
2N/A if (minor == NULL || user_ok == NULL)
2N/A return (GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A *user_ok = 0;
2N/A
2N/A kret = krb5_gss_init_context(&ctxt);
2N/A if (kret) {
2N/A *minor = kret;
2N/A return (GSS_S_FAILURE);
2N/A }
2N/A
2N/A if (! kg_validate_name(pname)) {
2N/A *minor = (OM_uint32) G_VALIDATE_FAILED;
2N/A krb5_free_context(ctxt);
2N/A return (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
2N/A }
2N/A
2N/A if (krb5_kuserok(ctxt, ((krb5_gss_name_t)pname)->princ, user)) {
2N/A *user_ok = 1;
2N/A }
2N/A
2N/A krb5_free_context(ctxt);
2N/A return (GSS_S_COMPLETE);
2N/A}
2N/A
2N/A#else /* _WIN32 */
2N/A
2N/A/*
2N/A * If the given Kerberos name "server" translates to the same name as "luser"
2N/A * (using * krb5_aname_to_lname()), returns TRUE.
2N/A */
2N/Akrb5_boolean KRB5_CALLCONV
2N/Akrb5_kuserok(context, principal, luser)
2N/A krb5_context context;
2N/A krb5_principal principal;
2N/A const char *luser;
2N/A{
2N/A char kuser[50];
2N/A
2N/A if (krb5_aname_to_localname(context, principal, sizeof(kuser), kuser))
2N/A return FALSE;
2N/A
2N/A if (strcmp(kuser, luser) == 0)
2N/A return TRUE;
2N/A
2N/A return FALSE;
2N/A}
2N/A#endif /* _WIN32 */