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