#include <krb5.h>
#include <errno.h>
#include <netdb.h>
#include <strings.h>
#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include "kt_solaris.h"
#define AES128 ENCTYPE_AES128_CTS_HMAC_SHA1_96
#define AES256 ENCTYPE_AES256_CTS_HMAC_SHA1_96
#define DES3 ENCTYPE_DES3_CBC_SHA1
#define AES_ENTRIES 2
#define HOST_TRUNC 15
#define SVC_ENTRIES 4
static krb5_error_code
kt_open(krb5_context ctx, krb5_keytab *kt)
{
krb5_error_code code;
char buf[MAX_KEYTAB_NAME_LEN], ktstr[MAX_KEYTAB_NAME_LEN];
memset(buf, 0, sizeof (buf));
memset(ktstr, 0, sizeof (ktstr));
if ((code = krb5_kt_default_name(ctx, buf, sizeof (buf))) != 0)
return (code);
if (strncmp(buf, "FILE:", strlen("FILE:")) == 0)
(void) snprintf(ktstr, sizeof (ktstr), "WR%s", buf);
else if (strncmp(buf, "WRFILE:", strlen("WRFILE:")) == 0)
(void) snprintf(ktstr, sizeof (ktstr), "%s", buf);
else
return (EINVAL);
return (krb5_kt_resolve(ctx, ktstr, kt));
}
static krb5_error_code
kt_add_entry(krb5_context ctx, krb5_keytab kt, const krb5_principal princ,
const krb5_principal svc_princ, krb5_enctype enctype, krb5_kvno kvno,
const char *pw)
{
krb5_keytab_entry entry;
krb5_data password, salt;
krb5_keyblock key;
krb5_error_code code;
memset(&entry, 0, sizeof (entry));
memset(&key, 0, sizeof (krb5_keyblock));
password.length = strlen(pw);
password.data = (char *)pw;
if ((code = krb5_principal2salt(ctx, svc_princ, &salt)) != 0) {
return (code);
}
if ((krb5_c_string_to_key(ctx, enctype, &password, &salt, &key)) != 0)
goto cleanup;
entry.key = key;
entry.vno = kvno;
entry.principal = princ;
code = krb5_kt_add_entry(ctx, kt, &entry);
cleanup:
krb5_xfree(salt.data);
krb5_free_keyblock_contents(ctx, &key);
return (code);
}
krb5_error_code
krb5_kt_add_ad_entries(krb5_context ctx, char **sprincs_str, char *domain,
krb5_kvno kvno, uint_t flags, char *password)
{
krb5_principal princ = NULL, salt = NULL, f_princ = NULL;
krb5_keytab kt = NULL;
krb5_enctype *enctypes = NULL, *tenctype, penctype = 0;
char **tprinc, *ptr, *token, *t_host = NULL, *realm;
char localname[MAXHOSTNAMELEN];
krb5_error_code code;
krb5_boolean similar;
uint_t t_len;
assert(ctx != NULL && sprincs_str != NULL && *sprincs_str != NULL);
assert(password != NULL && domain != NULL);
if ((code = krb5_parse_name(ctx, *sprincs_str, &f_princ)) != 0)
return (code);
if (krb5_princ_realm(ctx, f_princ)->length == 0) {
code = EINVAL;
goto cleanup;
}
realm = krb5_princ_realm(ctx, f_princ)->data;
if (gethostname(localname, MAXHOSTNAMELEN) != 0) {
code = errno;
goto cleanup;
}
token = localname;
if ((ptr = strchr(token, '.')) != NULL)
ptr = '\0';
for (ptr = token; *ptr; ptr++)
*ptr = tolower(*ptr);
t_len = snprintf(NULL, 0, "host/%.*s.%s@%s", HOST_TRUNC, token, domain,
realm) + 1;
if ((t_host = malloc(t_len)) == NULL) {
code = ENOMEM;
goto cleanup;
}
(void) snprintf(t_host, t_len, "host/%.*s.%s@%s", HOST_TRUNC, token,
domain, realm);
if ((code = krb5_parse_name(ctx, t_host, &salt)) != 0)
goto cleanup;
if ((code = kt_open(ctx, &kt)) != 0)
goto cleanup;
code = krb5_get_permitted_enctypes(ctx, &enctypes);
if (code != 0 || *enctypes == 0)
goto cleanup;
for (tprinc = sprincs_str; *tprinc; tprinc++) {
if ((code = krb5_parse_name(ctx, *tprinc, &princ)) != 0)
goto cleanup;
for (tenctype = enctypes; *tenctype; tenctype++) {
if ((!(flags & KRB5_KT_FLAG_AES_SUPPORT) &&
(*tenctype == AES128 || *tenctype == AES256)) ||
(*tenctype == DES3)) {
continue;
}
if (penctype) {
code = krb5_c_enctype_compare(ctx, *tenctype,
penctype, &similar);
if (code != 0)
goto cleanup;
else if (similar)
continue;
}
code = kt_add_entry(ctx, kt, princ, salt, *tenctype,
kvno, password);
if (code != 0)
goto cleanup;
penctype = *tenctype;
}
krb5_free_principal(ctx, princ);
princ = NULL;
}
cleanup:
if (f_princ != NULL)
krb5_free_principal(ctx, f_princ);
if (salt != NULL)
krb5_free_principal(ctx, salt);
if (t_host != NULL)
free(t_host);
if (kt != NULL)
(void) krb5_kt_close(ctx, kt);
if (enctypes != NULL)
krb5_free_ktypes(ctx, enctypes);
if (princ != NULL)
krb5_free_principal(ctx, princ);
return (code);
}
#define PRINCIPAL 0
#define REALM 1
static krb5_error_code
kt_remove_by_key(krb5_context ctx, char *key, uint_t type)
{
krb5_error_code code;
krb5_kt_cursor cursor;
krb5_keytab_entry entry;
krb5_keytab kt = NULL;
krb5_principal svc_princ = NULL;
krb5_principal_data realm_data;
boolean_t found = FALSE;
assert(ctx != NULL && key != NULL);
if (type == REALM) {
krb5_princ_realm(ctx, &realm_data)->length = strlen(key);
krb5_princ_realm(ctx, &realm_data)->data = key;
} else if (type == PRINCIPAL) {
if ((code = krb5_parse_name(ctx, key, &svc_princ)) != 0)
goto cleanup;
} else
return (EINVAL);
if ((code = kt_open(ctx, &kt)) != 0)
goto cleanup;
if ((code = krb5_kt_start_seq_get(ctx, kt, &cursor)) != 0)
goto cleanup;
while ((code = krb5_kt_next_entry(ctx, kt, &entry, &cursor)) == 0) {
if (type == PRINCIPAL && krb5_principal_compare(ctx, svc_princ,
entry.principal)) {
found = TRUE;
} else if (type == REALM && krb5_realm_compare(ctx, &realm_data,
entry.principal)) {
found = TRUE;
}
if (found == TRUE) {
code = krb5_kt_end_seq_get(ctx, kt, &cursor);
if (code != 0) {
krb5_kt_free_entry(ctx, &entry);
goto cleanup;
}
code = krb5_kt_remove_entry(ctx, kt, &entry);
if (code != 0) {
krb5_kt_free_entry(ctx, &entry);
goto cleanup;
}
code = krb5_kt_start_seq_get(ctx, kt, &cursor);
if (code != 0) {
krb5_kt_free_entry(ctx, &entry);
goto cleanup;
}
found = FALSE;
}
krb5_kt_free_entry(ctx, &entry);
}
if (code && code != KRB5_KT_END)
goto cleanup;
code = krb5_kt_end_seq_get(ctx, kt, &cursor);
cleanup:
if (svc_princ != NULL)
krb5_free_principal(ctx, svc_princ);
if (kt != NULL)
(void) krb5_kt_close(ctx, kt);
return (code);
}
krb5_error_code
krb5_kt_remove_by_realm(krb5_context ctx, char *realm)
{
return (kt_remove_by_key(ctx, realm, REALM));
}
krb5_error_code
krb5_kt_remove_by_svcprinc(krb5_context ctx, char *sprinc_str)
{
return (kt_remove_by_key(ctx, sprinc_str, PRINCIPAL));
}
krb5_error_code
krb5_kt_ad_validate(krb5_context ctx, char *sprinc_str, uint_t flags,
boolean_t *valid)
{
krb5_error_code code;
krb5_kt_cursor cursor;
krb5_keytab_entry entry;
krb5_keytab kt = NULL;
krb5_principal svc_princ = NULL;
krb5_enctype *enctypes, *tenctype, penctype = 0;
boolean_t ck_aes = FALSE;
uint_t aes_count = 0, kt_entries = 0;
krb5_boolean similar;
assert(ctx != NULL && sprinc_str != NULL && valid != NULL);
*valid = FALSE;
ck_aes = flags & KRB5_KT_FLAG_AES_SUPPORT;
if ((code = krb5_parse_name(ctx, sprinc_str, &svc_princ)) != 0)
goto cleanup;
if ((code = kt_open(ctx, &kt)) != 0)
goto cleanup;
code = krb5_get_permitted_enctypes(ctx, &enctypes);
if (code != 0 || *enctypes == 0)
goto cleanup;
if ((code = krb5_kt_start_seq_get(ctx, kt, &cursor)) != 0)
goto cleanup;
while ((code = krb5_kt_next_entry(ctx, kt, &entry, &cursor)) == 0) {
if (krb5_principal_compare(ctx, svc_princ, entry.principal)) {
for (tenctype = enctypes; *tenctype; tenctype++) {
if (penctype) {
code = krb5_c_enctype_compare(ctx,
*tenctype, penctype, &similar);
if (code != 0) {
krb5_kt_free_entry(ctx, &entry);
goto cleanup;
} else if (similar)
continue;
}
if ((*tenctype != DES3) &&
(entry.key.enctype == *tenctype)) {
kt_entries++;
}
penctype = *tenctype;
}
if ((entry.key.enctype == AES128) ||
(entry.key.enctype == AES256)) {
aes_count++;
}
}
krb5_kt_free_entry(ctx, &entry);
}
if (code && code != KRB5_KT_END)
goto cleanup;
if ((code = krb5_kt_end_seq_get(ctx, kt, &cursor)))
goto cleanup;
if (ck_aes == TRUE) {
if ((kt_entries != SVC_ENTRIES) || (aes_count != AES_ENTRIES))
goto cleanup;
} else if (kt_entries != (SVC_ENTRIES - AES_ENTRIES))
goto cleanup;
*valid = TRUE;
cleanup:
if (svc_princ != NULL)
krb5_free_principal(ctx, svc_princ);
if (kt != NULL)
(void) krb5_kt_close(ctx, kt);
if (enctypes != NULL)
krb5_free_ktypes(ctx, enctypes);
return (code);
}