2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/krb5/krb/conv_princ.c
2N/A *
2N/A * Copyright 1992 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 * Build a principal from a V4 specification, or separate a V5
2N/A * principal into name, instance, and realm.
2N/A *
2N/A * NOTE: This is highly site specific, and is only really necessary
2N/A * for sites who need to convert from V4 to V5. It is used by both
2N/A * the KDC and the kdb5_convert program. Since its use is highly
2N/A * specialized, the necesary information is just going to be
2N/A * hard-coded in this file.
2N/A */
2N/A
2N/A#include "k5-int.h"
2N/A#include <string.h>
2N/A#include <ctype.h>
2N/A
2N/A/* The maximum sizes for V4 aname, realm, sname, and instance +1 */
2N/A/* Taken from krb.h */
2N/A#define ANAME_SZ 40
2N/A#define REALM_SZ 40
2N/A#define SNAME_SZ 40
2N/A#define INST_SZ 40
2N/A
2N/Astruct krb_convert {
2N/A char *v4_str;
2N/A char *v5_str;
2N/A unsigned int flags : 8;
2N/A unsigned int len : 8;
2N/A};
2N/A
2N/A#define DO_REALM_CONVERSION 0x00000001
2N/A
2N/A/*
2N/A * Kadmin doesn't do realm conversion because it's currently
2N/A * kadmin/REALM.NAME. Zephyr doesn't because it's just zephyr/zephyr.
2N/A *
2N/A * "Realm conversion" is a bit of a misnomer; really, the v5 name is
2N/A * using a FQDN or something that looks like it, where the v4 name is
2N/A * just using the first label. Sometimes that second principal name
2N/A * component is a hostname, sometimes the realm name, sometimes it's
2N/A * neither.
2N/A *
2N/A * This list should probably be more configurable, and more than
2N/A * likely on a per-realm basis, so locally-defined services can be
2N/A * added, or not.
2N/A */
2N/Astatic const struct krb_convert sconv_list[] = {
2N/A /* Realm conversion, Change service name */
2N/A#define RC(V5NAME,V4NAME) { V5NAME, V4NAME, DO_REALM_CONVERSION, sizeof(V5NAME)-1 }
2N/A /* Realm conversion */
2N/A#define R(NAME) { NAME, NAME, DO_REALM_CONVERSION, sizeof(NAME)-1 }
2N/A /* No Realm conversion */
2N/A#define NR(NAME) { NAME, NAME, 0, sizeof(NAME)-1 }
2N/A
2N/A NR("kadmin"),
2N/A RC("rcmd", "host"),
2N/A R("discuss"),
2N/A R("rvdsrv"),
2N/A R("sample"),
2N/A R("olc"),
2N/A R("pop"),
2N/A R("sis"),
2N/A R("rfs"),
2N/A R("imap"),
2N/A R("ftp"),
2N/A R("ecat"),
2N/A R("daemon"),
2N/A R("gnats"),
2N/A R("moira"),
2N/A R("prms"),
2N/A R("mandarin"),
2N/A R("register"),
2N/A R("changepw"),
2N/A R("sms"),
2N/A R("afpserver"),
2N/A R("gdss"),
2N/A R("news"),
2N/A R("abs"),
2N/A R("nfs"),
2N/A R("tftp"),
2N/A NR("zephyr"),
2N/A R("http"),
2N/A R("khttp"),
2N/A R("pgpsigner"),
2N/A R("irc"),
2N/A R("mandarin-agent"),
2N/A R("write"),
2N/A R("palladium"),
2N/A {0, 0, 0, 0},
2N/A#undef R
2N/A#undef RC
2N/A#undef NR
2N/A};
2N/A
2N/A/*
2N/A * char *strnchr(s, c, n)
2N/A * char *s;
2N/A * char c;
2N/A * unsigned int n;
2N/A *
2N/A * returns a pointer to the first occurrence of character c in the
2N/A * string s, or a NULL pointer if c does not occur in in the string;
2N/A * however, at most the first n characters will be considered.
2N/A *
2N/A * This falls in the "should have been in the ANSI C library"
2N/A * category. :-)
2N/A */
2N/Astatic char *strnchr(register char *s, register int c,
2N/A register unsigned int n)
2N/A{
2N/A if (n < 1)
2N/A return 0;
2N/A
2N/A while (n-- && *s) {
2N/A if (*s == c)
2N/A return s;
2N/A s++;
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/A
2N/A/* XXX This calls for a new error code */
2N/A#define KRB5_INVALID_PRINCIPAL KRB5_LNAME_BADFORMAT
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_524_conv_principal(krb5_context context, krb5_const_principal princ,
2N/A char *name, char *inst, char *realm)
2N/A{
2N/A const struct krb_convert *p;
2N/A const krb5_data *compo;
2N/A char *c, *tmp_realm, *tmp_prealm;
2N/A unsigned int tmp_realm_len;
2N/A int retval;
2N/A
2N/A if (context->profile == 0)
2N/A return KRB5_CONFIG_CANTOPEN;
2N/A
2N/A *name = *inst = '\0';
2N/A switch (krb5_princ_size(context, princ)) {
2N/A case 2:
2N/A /* Check if this principal is listed in the table */
2N/A compo = krb5_princ_component(context, princ, 0);
2N/A p = sconv_list;
2N/A while (p->v4_str) {
2N/A if (p->len == compo->length
2N/A && memcmp(p->v5_str, compo->data, compo->length) == 0) {
2N/A /*
2N/A * It is, so set the new name now, and chop off
2N/A * instance's domain name if requested.
2N/A */
2N/A if (strlcpy(name, p->v4_str, ANAME_SZ) >= ANAME_SZ)
2N/A return KRB5_INVALID_PRINCIPAL;
2N/A if (p->flags & DO_REALM_CONVERSION) {
2N/A compo = krb5_princ_component(context, princ, 1);
2N/A c = strnchr(compo->data, '.', compo->length);
2N/A if (!c || (c - compo->data) >= INST_SZ - 1)
2N/A return KRB5_INVALID_PRINCIPAL;
2N/A memcpy(inst, compo->data, (size_t) (c - compo->data));
2N/A inst[c - compo->data] = '\0';
2N/A }
2N/A break;
2N/A }
2N/A p++;
2N/A }
2N/A /* If inst isn't set, the service isn't listed in the table, */
2N/A /* so just copy it. */
2N/A if (*inst == '\0') {
2N/A compo = krb5_princ_component(context, princ, 1);
2N/A if (compo->length >= INST_SZ - 1)
2N/A return KRB5_INVALID_PRINCIPAL;
2N/A memcpy(inst, compo->data, compo->length);
2N/A inst[compo->length] = '\0';
2N/A }
2N/A /* fall through */
2N/A case 1:
2N/A /* name may have been set above; otherwise, just copy it */
2N/A if (*name == '\0') {
2N/A compo = krb5_princ_component(context, princ, 0);
2N/A if (compo->length >= ANAME_SZ)
2N/A return KRB5_INVALID_PRINCIPAL;
2N/A memcpy(name, compo->data, compo->length);
2N/A name[compo->length] = '\0';
2N/A }
2N/A break;
2N/A default:
2N/A return KRB5_INVALID_PRINCIPAL;
2N/A }
2N/A
2N/A compo = krb5_princ_realm(context, princ);
2N/A
2N/A tmp_prealm = malloc(compo->length + 1);
2N/A if (tmp_prealm == NULL)
2N/A return ENOMEM;
2N/A strncpy(tmp_prealm, compo->data, compo->length);
2N/A tmp_prealm[compo->length] = '\0';
2N/A
2N/A /* Ask for v4_realm corresponding to
2N/A krb5 principal realm from krb5.conf realms stanza */
2N/A
2N/A retval = profile_get_string(context->profile, KRB5_CONF_REALMS,
2N/A tmp_prealm, KRB5_CONF_V4_REALM, 0,
2N/A &tmp_realm);
2N/A free(tmp_prealm);
2N/A if (retval) {
2N/A return retval;
2N/A } else {
2N/A if (tmp_realm == 0) {
2N/A if (compo->length > REALM_SZ - 1)
2N/A return KRB5_INVALID_PRINCIPAL;
2N/A strncpy(realm, compo->data, compo->length);
2N/A realm[compo->length] = '\0';
2N/A } else {
2N/A tmp_realm_len = strlen(tmp_realm);
2N/A if (tmp_realm_len > REALM_SZ - 1)
2N/A return KRB5_INVALID_PRINCIPAL;
2N/A strncpy(realm, tmp_realm, tmp_realm_len);
2N/A realm[tmp_realm_len] = '\0';
2N/A profile_release_string(tmp_realm);
2N/A }
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_425_conv_principal(krb5_context context, const char *name,
2N/A const char *instance, const char *realm,
2N/A krb5_principal *princ)
2N/A{
2N/A const struct krb_convert *p;
2N/A char buf[256]; /* V4 instances are limited to 40 characters */
2N/A krb5_error_code retval;
2N/A char *domain, *cp;
2N/A char **full_name = 0;
2N/A const char *names[5], *names2[2];
2N/A void* iterator = NULL;
2N/A char** v4realms = NULL;
2N/A char* realm_name = NULL;
2N/A char* dummy_value = NULL;
2N/A
2N/A /* First, convert the realm, since the v4 realm is not necessarily the same as the v5 realm
2N/A To do that, iterate over all the realms in the config file, looking for a matching
2N/A v4_realm line */
2N/A names2 [0] = KRB5_CONF_REALMS;
2N/A names2 [1] = NULL;
2N/A retval = profile_iterator_create (context -> profile, names2, PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY, &iterator);
2N/A while (retval == 0) {
2N/A retval = profile_iterator (&iterator, &realm_name, &dummy_value);
2N/A if ((retval == 0) && (realm_name != NULL)) {
2N/A names [0] = KRB5_CONF_REALMS;
2N/A names [1] = realm_name;
2N/A names [2] = KRB5_CONF_V4_REALM;
2N/A names [3] = NULL;
2N/A
2N/A retval = profile_get_values (context -> profile, names, &v4realms);
2N/A if ((retval == 0) && (v4realms != NULL) && (v4realms [0] != NULL) && (strcmp (v4realms [0], realm) == 0)) {
2N/A realm = realm_name;
2N/A break;
2N/A } else if (retval == PROF_NO_RELATION) {
2N/A /* If it's not found, just keep going */
2N/A retval = 0;
2N/A }
2N/A } else if ((retval == 0) && (realm_name == NULL)) {
2N/A break;
2N/A }
2N/A if (v4realms != NULL) {
2N/A profile_free_list(v4realms);
2N/A v4realms = NULL;
2N/A }
2N/A if (realm_name != NULL) {
2N/A profile_release_string (realm_name);
2N/A realm_name = NULL;
2N/A }
2N/A if (dummy_value != NULL) {
2N/A profile_release_string (dummy_value);
2N/A dummy_value = NULL;
2N/A }
2N/A }
2N/A
2N/A if (instance) {
2N/A if (instance[0] == '\0') {
2N/A instance = 0;
2N/A goto not_service;
2N/A }
2N/A p = sconv_list;
2N/A while (1) {
2N/A if (!p->v4_str)
2N/A goto not_service;
2N/A if (!strcmp(p->v4_str, name))
2N/A break;
2N/A p++;
2N/A }
2N/A name = p->v5_str;
2N/A if ((p->flags & DO_REALM_CONVERSION) && !strchr(instance, '.')) {
2N/A names[0] = KRB5_CONF_REALMS;
2N/A names[1] = realm;
2N/A names[2] = KRB5_CONF_V4_INSTANCE_CONVERT;
2N/A names[3] = instance;
2N/A names[4] = 0;
2N/A retval = profile_get_values(context->profile, names, &full_name);
2N/A if (retval == 0 && full_name && full_name[0]) {
2N/A instance = full_name[0];
2N/A } else {
2N/A strncpy(buf, instance, sizeof(buf));
2N/A buf[sizeof(buf) - 1] = '\0';
2N/A retval = krb5_get_realm_domain(context, realm, &domain);
2N/A if (retval)
2N/A return retval;
2N/A if (domain) {
2N/A for (cp = domain; *cp; cp++)
2N/A if (isupper((unsigned char) (*cp)))
2N/A *cp = tolower((unsigned char) *cp);
2N/A strncat(buf, ".", sizeof(buf) - 1 - strlen(buf));
2N/A strncat(buf, domain, sizeof(buf) - 1 - strlen(buf));
2N/A free(domain);
2N/A }
2N/A instance = buf;
2N/A }
2N/A }
2N/A }
2N/A
2N/Anot_service:
2N/A retval = krb5_build_principal(context, princ, strlen(realm), realm, name,
2N/A instance, NULL);
2N/A if (iterator) profile_iterator_free (&iterator);
2N/A if (full_name) profile_free_list(full_name);
2N/A if (v4realms) profile_free_list(v4realms);
2N/A if (realm_name) profile_release_string (realm_name);
2N/A if (dummy_value) profile_release_string (dummy_value);
2N/A return retval;
2N/A}