#include "k5-int.h"
#include "int-proto.h"
static krb5_error_code
allocate_princ(krb5_context context, const char *name, krb5_boolean enterprise,
krb5_principal *princ_out, krb5_boolean *has_realm_out)
{
krb5_error_code ret;
const char *p;
krb5_boolean first_at = TRUE;
krb5_principal princ = NULL;
krb5_data *cur_data, *new_comps;
krb5_int32 i;
*princ_out = NULL;
*has_realm_out = FALSE;
princ = k5alloc(sizeof(*princ), &ret);
if (princ == NULL)
goto cleanup;
princ->data = k5alloc(sizeof(*princ->data), &ret);
if (princ->data == NULL)
goto cleanup;
princ->realm = empty_data();
princ->data[0] = empty_data();
princ->length = 1;
cur_data = &princ->data[0];
for (p = name; *p != '\0'; p++) {
if (*p == '/' && !enterprise) {
if (cur_data == &princ->realm) {
ret = KRB5_PARSE_MALFORMED;
goto cleanup;
}
new_comps = realloc(princ->data,
(princ->length + 1) * sizeof(*princ->data));
if (new_comps == NULL) {
ret = ENOMEM;
goto cleanup;
}
princ->data = new_comps;
princ->length++;
cur_data = &princ->data[princ->length - 1];
*cur_data = empty_data();
} else if (*p == '@' && (!enterprise || !first_at)) {
if (cur_data == &princ->realm) {
ret = KRB5_PARSE_MALFORMED;
goto cleanup;
}
cur_data = &princ->realm;
} else {
cur_data->length++;
if (*p == '@' && enterprise)
first_at = FALSE;
if (*p == '\\') {
if (*++p == '\0') {
ret = KRB5_PARSE_MALFORMED;
goto cleanup;
}
}
}
}
for (i = 0; i < princ->length; i++) {
princ->data[i].data = k5alloc(princ->data[i].length + 1, &ret);
if (princ->data[i].data == NULL)
goto cleanup;
}
princ->realm.data = k5alloc(princ->realm.length + 1, &ret);
if (princ->realm.data == NULL)
goto cleanup;
*princ_out = princ;
*has_realm_out = (cur_data == &princ->realm);
princ = NULL;
cleanup:
krb5_free_principal(context, princ);
return ret;
}
static void
parse_name_into_princ(const char *name, krb5_boolean enterprise,
krb5_principal princ)
{
const char *p;
char c;
krb5_boolean first_at = TRUE;
krb5_data *cur_data = princ->data;
unsigned int pos = 0;
for (p = name; *p != '\0'; p++) {
if (*p == '/' && !enterprise) {
assert(pos == cur_data->length);
assert(cur_data != &princ->realm);
assert(cur_data - princ->data + 1 < princ->length);
cur_data++;
pos = 0;
} else if (*p == '@' && (!enterprise || !first_at)) {
assert(pos == cur_data->length);
cur_data = &princ->realm;
pos = 0;
} else {
if (*p == '@' && enterprise)
first_at = FALSE;
c = *p;
if (c == '\\') {
c = *++p;
if (c == 'n')
c = '\n';
else if (c == 't')
c = '\t';
else if (c == 'b')
c = '\b';
else if (c == '0')
c = '\0';
}
assert(pos < cur_data->length);
cur_data->data[pos++] = c;
}
}
assert(pos == cur_data->length);
}
krb5_error_code KRB5_CALLCONV
krb5_parse_name_flags(krb5_context context, const char *name,
int flags, krb5_principal *principal_out)
{
krb5_error_code ret;
krb5_principal princ = NULL;
char *default_realm;
krb5_boolean has_realm;
krb5_boolean enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
krb5_boolean require_realm = (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM);
krb5_boolean no_realm = (flags & KRB5_PRINCIPAL_PARSE_NO_REALM);
krb5_boolean ignore_realm = (flags & KRB5_PRINCIPAL_PARSE_IGNORE_REALM);
krb5_boolean no_def_realm = (flags & KRB5_PRINCIPAL_PARSE_NO_DEF_REALM);
*principal_out = NULL;
ret = allocate_princ(context, name, enterprise, &princ, &has_realm);
if (ret)
goto cleanup;
parse_name_into_princ(name, enterprise, princ);
if (!has_realm) {
if (require_realm) {
ret = KRB5_PARSE_MALFORMED;
k5_setmsg(context, ret,
_("Principal %s is missing required realm"), name);
goto cleanup;
}
if (!no_realm && !ignore_realm && !no_def_realm) {
ret = krb5_get_default_realm(context, &default_realm);
if (ret)
goto cleanup;
krb5_free_data_contents(context, &princ->realm);
princ->realm = string2data(default_realm);
}
} else if (no_realm) {
ret = KRB5_PARSE_MALFORMED;
k5_setmsg(context, ret, _("Principal %s has realm present"), name);
goto cleanup;
} else if (ignore_realm) {
krb5_free_data_contents(context, &princ->realm);
princ->realm = empty_data();
}
princ->type = (enterprise) ? KRB5_NT_ENTERPRISE_PRINCIPAL :
k5_infer_principal_type(princ);
princ->magic = KV5M_PRINCIPAL;
*principal_out = princ;
princ = NULL;
cleanup:
krb5_free_principal(context, princ);
return ret;
}
krb5_error_code KRB5_CALLCONV
krb5_parse_name(krb5_context context, const char *name,
krb5_principal *principal_out)
{
return krb5_parse_name_flags(context, name, 0, principal_out);
}