static char rcsid[] __attribute__((unused)) =
"$Id: afskrb.c,v 1.4 2005/03/05 00:37:35 dasenbro Exp $";
#include <config.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/param.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
#ifdef AFSPTS_USE_KRB5
#include <krb5.h>
#else
#include <krb.h>
#endif
#include "auth_pts.h"
#include "exitcodes.h"
#include "libconfig.h"
#include "strhash.h"
#include "xmalloc.h"
#include <des.h>
#include <afs/ptserver.h>
#include <afs/pterror.h>
#include <afs/cellconfig.h>
#include <rx/rxkad.h>
#include <afs/auth.h>
#ifndef AFSCONF_CLIENTNAME
#include <afs/dirpath.h>
#define AFSCONF_CLIENTNAME AFSDIR_CLIENT_ETC_DIRPATH
#endif
#if PTS_DB_KEYSIZE < PR_MAXNAMELEN
#error PTS_DB_KEYSIZE is smaller than PR_MAXNAMELEN
#endif
static const char *localrealms = NULL;
int is_local_realm(const char *realm)
{
const char *val = localrealms;
if(!val || !realm) return 0;
while (*val) {
char buf[1024];
size_t len;
char *p;
for (p = (char *) val; *p && !isspace((int) *p); p++);
len = p-val;
if(len >= sizeof(buf))
len = sizeof(buf) - 1;
memcpy(buf, val, len);
buf[len] = '\0';
if (!strcasecmp(realm,buf)) {
return 1;
}
val = p;
while (*val && isspace((int) *val)) val++;
}
return 0;
}
#ifdef AFSPTS_USE_KRB5
static char *afspts_canonifyid(const char *identifier, size_t len)
{
static char *retbuf = NULL;
krb5_context context;
krb5_principal princ, princ_dummy;
char *realm;
char *realmbegin;
int striprealm = 0;
if(retbuf) free(retbuf);
retbuf = NULL;
if(!identifier) return NULL;
if(!len) len = strlen(identifier);
if (strcasecmp(identifier, "anonymous") == 0)
return "anonymous";
if (strcasecmp(identifier, "anyone") == 0)
return "anyone";
if (krb5_init_context(&context))
return NULL;
if (krb5_parse_name(context,identifier,&princ))
{
krb5_free_context(context);
return NULL;
}
if(config_getswitch(IMAPOPT_PTSKRB5_STRIP_DEFAULT_REALM)) {
if (krb5_get_default_realm(context,&realm))
{
krb5_free_principal(context,princ);
krb5_free_context(context);
return NULL;
}
if (krb5_build_principal(context,&princ_dummy,
strlen(realm),realm,"dummy",0))
{
krb5_free_principal(context,princ);
krb5_free_context(context);
free(realm);
return NULL;
}
if (krb5_realm_compare(context,princ,princ_dummy))
{
striprealm = 1;
}
krb5_free_principal(context,princ_dummy);
free(realm);
}
if (config_getswitch(IMAPOPT_PTSKRB5_CONVERT524)) {
char nbuf[64], ibuf[64], rbuf[64];
if (krb5_524_conv_principal(context, princ, nbuf, ibuf, rbuf)) {
krb5_free_principal(context,princ);
krb5_free_context(context);
return NULL;
}
retbuf = xmalloc(3*64 + 3);
sprintf(retbuf, "%s%s%s%s%s", nbuf,
ibuf[0] ? "." : "", ibuf,
rbuf[0] ? "@" : "", rbuf);
} else {
if (krb5_unparse_name(context,princ,&retbuf))
{
krb5_free_principal(context,princ);
krb5_free_context(context);
return NULL;
}
}
realmbegin = strrchr(retbuf, '@');
if(realmbegin) {
if(!striprealm) {
realm = realmbegin+1;
if(is_local_realm(realm))
striprealm = 1;
}
if(striprealm) {
*realmbegin = '\0';
} else {
while(*(++realmbegin)) {
*realmbegin = toupper(*realmbegin);
}
}
}
krb5_free_principal(context,princ);
krb5_free_context(context);
return retbuf;
}
#else
# if PTS_DB_KEYSIZE < MAX_K_NAME_SZ
# error PTS_DB_KEYSIZE is smaller than MAX_K_NAME_SZ
# endif
# ifndef KRB_MAPNAME
# define KRB_MAPNAME (SYSCONFDIR "/krb.equiv")
# endif
static int parse_krbequiv_line(const char *src,
char *principal,
char *localuser)
{
int i;
while (isspace(*src)) src++;
if (!*src) return 0;
for (i = 0; *src && !isspace(*src); i++) {
if (i >= MAX_K_NAME_SZ) return 0;
*principal++ = *src++;
}
*principal = 0;
if (!isspace(*src)) return 0;
while (isspace(*src)) src++;
if (!*src) return 0;
for (i = 0; *src && !isspace(*src); i++) {
if (i >= MAX_K_NAME_SZ) return 0;
*localuser++ = *src++;
}
*localuser = 0;
return 1;
}
static char *auth_map_krbid(const char *real_aname,
const char *real_inst,
const char *real_realm)
{
static char localuser[MAX_K_NAME_SZ + 1];
char principal[MAX_K_NAME_SZ + 1];
char aname[ANAME_SZ];
char inst[INST_SZ];
char realm[REALM_SZ];
char lrealm[REALM_SZ];
char krbhst[MAX_HSTNM];
char *p;
char buf[1024];
FILE *mapfile;
if (!(mapfile = fopen(KRB_MAPNAME, "r"))) {
return 0;
}
for (;;) {
if (!fgets(buf, sizeof(buf), mapfile)) break;
if (parse_krbequiv_line(buf, principal, localuser) == 0 ||
kname_parse(aname, inst, realm, principal) != 0) {
continue;
}
if (!strcmp(aname, real_aname) && !strcmp(inst, real_inst) &&
!strcmp(realm, real_realm)) {
fclose(mapfile);
aname[0] = inst[0] = realm[0] = '\0';
if (kname_parse(aname, inst, realm, localuser) != 0) {
return 0;
}
for (p = realm; *p; p++) {
if (islower(*p)) *p = toupper(*p);
}
if (*realm) {
if (krb_get_lrealm(lrealm,1) == 0 &&
strcmp(lrealm, realm) == 0) {
*realm = 0;
}
else if (krb_get_krbhst(krbhst, realm, 1)) {
return 0;
}
}
strcpy(localuser, aname);
if (*inst) {
strcat(localuser, ".");
strcat(localuser, inst);
}
if (*realm) {
strcat(localuser, "@");
strcat(localuser, realm);
}
return localuser;
}
}
fclose(mapfile);
return 0;
}
static char *afspts_canonifyid(const char *identifier, size_t len)
{
static char retbuf[MAX_K_NAME_SZ+1];
char aname[ANAME_SZ];
char inst[INST_SZ];
char realm[REALM_SZ];
char lrealm[REALM_SZ];
char krbhst[MAX_HSTNM];
char *canon_buf;
char *p;
if(!len) len = strlen(identifier);
canon_buf = xmalloc(len + 1);
memcpy(canon_buf, identifier, len);
canon_buf[len] = '\0';
aname[0] = inst[0] = realm[0] = '\0';
if (kname_parse(aname, inst, realm, canon_buf) != 0) {
free(canon_buf);
return 0;
}
free(canon_buf);
for (p = realm; *p; p++) {
if (islower(*p)) *p = toupper(*p);
}
if (*realm) {
if (krb_get_lrealm(lrealm,1) == 0 &&
strcmp(lrealm, realm) == 0) {
*realm = 0;
}
else if (krb_get_krbhst(krbhst, realm, 1)) {
return 0;
}
}
p = auth_map_krbid(aname, inst, realm);
if (p) {
strcpy(retbuf, p);
return retbuf;
}
strcpy(retbuf, aname);
if (*inst) {
strcat(retbuf, ".");
strcat(retbuf, inst);
}
if (*realm && !is_local_realm(realm)) {
strcat(retbuf, "@");
strcat(retbuf, realm);
}
return retbuf;
}
#endif
const char *ptsmodule_name = "afskrb";
void ptsmodule_init(void)
{
int r = pr_Initialize (1L, AFSCONF_CLIENTNAME, config_getstring(IMAPOPT_AFSPTS_MYCELL));
if (r) {
syslog(LOG_DEBUG, "pr_Initialize failed: %d", r);
fatal("pr_initialize failed", EC_TEMPFAIL);
}
localrealms = config_getstring(IMAPOPT_AFSPTS_LOCALREALMS);
return;
}
struct auth_state *ptsmodule_make_authstate(const char *identifier,
size_t size,
const char **reply, int *dsize)
{
const char *canon_id = afspts_canonifyid(identifier, size);
char canon_id_tmp[PTS_DB_KEYSIZE+1];
namelist groups;
int i, rc;
struct auth_state *newstate;
*reply = NULL;
size = strlen(canon_id);
memset(&groups, 0, sizeof(groups));
groups.namelist_len = 0;
groups.namelist_val = NULL;
strlcpy(canon_id_tmp,canon_id,sizeof(canon_id_tmp));
if ((rc = pr_ListMembers(canon_id_tmp, &groups))) {
pr_End();
rc = pr_Initialize (1L, AFSCONF_CLIENTNAME, 0);
if (rc) {
syslog(LOG_DEBUG, "pr_Initialize failed: %d", rc);
fatal("pr_Initialize failed", EC_TEMPFAIL);
}
rc = pr_ListMembers(canon_id_tmp, &groups);
}
if(rc) {
syslog(LOG_ERR, "pr_ListMembers %s: %s", canon_id, error_message(rc));
}
*dsize = sizeof(struct auth_state) +
(groups.namelist_len * sizeof(struct auth_ident));
newstate = (struct auth_state *) xmalloc(*dsize);
strcpy(newstate->userid.id, canon_id);
newstate->userid.hash = strhash(canon_id);
if (rc == PRPERM) {
newstate->mark = time(0) + 60 -
(config_getint(IMAPOPT_PTSCACHE_TIMEOUT) > 60)?
config_getint(IMAPOPT_PTSCACHE_TIMEOUT) : 60;
} else
newstate->mark = time(0);
newstate->ngroups = groups.namelist_len;
memset(newstate->groups, 0, newstate->ngroups * sizeof(struct auth_ident));
for (i = 0; i < newstate->ngroups; i++) {
strlcpy(newstate->groups[i].id, groups.namelist_val[i],
sizeof(newstate->groups[i].id));
newstate->groups[i].hash = strhash(groups.namelist_val[i]);
}
if (groups.namelist_val != NULL) {
free(groups.namelist_val);
}
return newstate;
}