#include "k5-int.h"
#include "cc-int.h"
#include <krb5/ccselect_plugin.h>
#include <ctype.h>
#ifndef _WIN32
#include <pwd.h>
static krb5_error_code
k5identity_init(krb5_context context, krb5_ccselect_moddata *data_out,
int *priority_out)
{
*data_out = NULL;
*priority_out = KRB5_CCSELECT_PRIORITY_AUTHORITATIVE;
return 0;
}
static krb5_boolean
fnmatch_data(const char *pattern, krb5_data *data, krb5_boolean fold_case)
{
krb5_error_code ret;
char *str, *p;
int res;
str = k5memdup0(data->data, data->length, &ret);
if (str == NULL)
return FALSE;
if (fold_case) {
for (p = str; *p != '\0'; p++) {
if (isupper((unsigned char)*p))
*p = tolower((unsigned char)*p);
}
}
res = fnmatch(pattern, str, 0);
free(str);
return (res == 0);
}
static krb5_boolean
check_constraint(krb5_context context, const char *name, const char *value,
krb5_principal server)
{
if (strcmp(name, "realm") == 0) {
return fnmatch_data(value, &server->realm, FALSE);
} else if (strcmp(name, "service") == 0) {
return (server->type == KRB5_NT_SRV_HST && server->length >= 2 &&
fnmatch_data(value, &server->data[0], FALSE));
} else if (strcmp(name, "host") == 0) {
return (server->type == KRB5_NT_SRV_HST && server->length >= 2 &&
fnmatch_data(value, &server->data[1], TRUE));
}
return FALSE;
}
static krb5_boolean
parse_line(krb5_context context, char *line, krb5_principal server,
krb5_principal *princ_out)
{
const char *whitespace = " \t\r\n";
char *princ, *princ_end, *field, *field_end, *sep;
*princ_out = NULL;
princ = line + strspn(line, whitespace);
if (*princ == '#')
return FALSE;
princ_end = princ + strcspn(princ, whitespace);
if (princ_end == princ)
return FALSE;
field = princ_end + strspn(princ_end, whitespace);
while (*field != '\0') {
field_end = field + strcspn(field, whitespace);
if (*field_end != '\0')
*field_end++ = '\0';
sep = strchr(field, '=');
if (sep == NULL)
return FALSE;
*sep = '\0';
if (!check_constraint(context, field, sep + 1, server))
return FALSE;
field = field_end + strspn(field_end, whitespace);
}
*princ_end = '\0';
return (krb5_parse_name(context, princ, princ_out) == 0);
}
static char *
get_homedir(krb5_context context)
{
const char *homedir = NULL;
char pwbuf[BUFSIZ];
struct passwd pwx, *pwd;
if (!context->profile_secure)
homedir = secure_getenv("HOME");
if (homedir == NULL) {
if (k5_getpwuid_r(geteuid(), &pwx, pwbuf, sizeof(pwbuf), &pwd) != 0)
return NULL;
homedir = pwd->pw_dir;
}
return strdup(homedir);
}
static krb5_error_code
k5identity_choose(krb5_context context, krb5_ccselect_moddata data,
krb5_principal server, krb5_ccache *cache_out,
krb5_principal *princ_out)
{
krb5_error_code ret;
krb5_principal princ = NULL;
char *filename, *homedir;
FILE *fp;
char buf[256];
*cache_out = NULL;
*princ_out = NULL;
homedir = get_homedir(context);
if (homedir == NULL)
return KRB5_PLUGIN_NO_HANDLE;
ret = k5_path_join(homedir, ".k5identity", &filename);
free(homedir);
if (ret)
return ret;
fp = fopen(filename, "r");
free(filename);
if (fp == NULL)
return KRB5_PLUGIN_NO_HANDLE;
while (fgets(buf, sizeof(buf), fp) != NULL) {
if (parse_line(context, buf, server, &princ))
break;
}
fclose(fp);
if (princ == NULL)
return KRB5_PLUGIN_NO_HANDLE;
ret = krb5_cc_cache_match(context, princ, cache_out);
if (ret == 0 || ret == KRB5_CC_NOTFOUND)
*princ_out = princ;
else
krb5_free_principal(context, princ);
return ret;
}
krb5_error_code
ccselect_k5identity_initvt(krb5_context context, int maj_ver, int min_ver,
krb5_plugin_vtable vtable)
{
krb5_ccselect_vtable vt;
if (maj_ver != 1)
return KRB5_PLUGIN_VER_NOTSUPP;
vt = (krb5_ccselect_vtable)vtable;
vt->name = "k5identity";
vt->init = k5identity_init;
vt->choose = k5identity_choose;
return 0;
}
#endif