Exemplo n.º 1
0
/* Construct a plugin mapping object.  path may be NULL (for a built-in
 * module), or may be relative to the plugin base directory. */
static krb5_error_code
make_plugin_mapping(krb5_context context, const char *name, size_t namelen,
                    const char *path, krb5_plugin_initvt_fn module,
                    struct plugin_mapping **map_out)
{
    krb5_error_code ret;
    struct plugin_mapping *map = NULL;

    /* Create the mapping entry. */
    map = k5alloc(sizeof(*map), &ret);
    if (map == NULL)
        return ret;

    map->modname = k5memdup0(name, namelen, &ret);
    if (map->modname == NULL)
        goto oom;
    if (path != NULL) {
        if (k5_path_join(context->plugin_base_dir, path, &map->dyn_path))
            goto oom;
    }
    map->module = module;
    *map_out = map;
    return 0;

oom:
    free_plugin_mapping(map);
    return ENOMEM;
}
Exemplo n.º 2
0
/*
 * Get realm list from "capaths" section of the profile.  Deliberately
 * returns success but leaves VALS null if profile_get_values() fails
 * by not finding anything.
 */
static krb5_error_code
rtree_capath_vals(krb5_context context,
                  const krb5_data *client,
                  const krb5_data *server,
                  char ***vals)
{
    krb5_error_code retval = 0;
    /* null-terminated realm names */
    char *clientz = NULL, *serverz = NULL;
    const char *key[4];

    *vals = NULL;

    clientz = k5memdup0(client->data, client->length, &retval);
    if (clientz == NULL)
        goto error;

    serverz = k5memdup0(server->data, server->length, &retval);
    if (serverz == NULL)
        goto error;

    key[0] = "capaths";
    key[1] = clientz;
    key[2] = serverz;
    key[3] = NULL;
    retval = profile_get_values(context->profile, key, vals);
    switch (retval) {
    case PROF_NO_SECTION:
    case PROF_NO_RELATION:
        /*
         * Not found; don't return an error.
         */
        retval = 0;
        break;
    default:
        break;
    }
error:
    free(clientz);
    free(serverz);
    return retval;
}
Exemplo n.º 3
0
OM_uint32 KRB5_CALLCONV
krb5_gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token,
                     gss_cred_id_t *cred_handle)
{
    OM_uint32 status = GSS_S_COMPLETE;
    krb5_context context;
    krb5_error_code ret;
    krb5_gss_cred_id_t cred;
    k5_json_value v = NULL;
    k5_json_array array;
    k5_json_string str;
    char *copy = NULL;

    ret = krb5_gss_init_context(&context);
    if (ret) {
        *minor_status = ret;
        return GSS_S_FAILURE;
    }

    /* Decode token. */
    copy = k5memdup0(token->value, token->length, &ret);
    if (copy == NULL) {
        status = GSS_S_FAILURE;
        *minor_status = ret;
        goto cleanup;
    }
    if (k5_json_decode(copy, &v))
        goto invalid;

    /* Decode the CRED_EXPORT_MAGIC array wrapper. */
    if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
        goto invalid;
    array = v;
    if (k5_json_array_length(array) != 2)
        goto invalid;
    str = check_element(array, 0, K5_JSON_TID_STRING);
    if (str == NULL ||
        strcmp(k5_json_string_utf8(str), CRED_EXPORT_MAGIC) != 0)
        goto invalid;
    if (json_to_kgcred(context, k5_json_array_get(array, 1), &cred))
        goto invalid;

    *cred_handle = (gss_cred_id_t)cred;

cleanup:
    free(copy);
    k5_json_release(v);
    krb5_free_context(context);
    return status;

invalid:
    status = GSS_S_DEFECTIVE_TOKEN;
    goto cleanup;
}
Exemplo n.º 4
0
/*
 * Hash extension records have the format:
 *  client = <empty string>
 *  server = HASH:<msghash> <clientlen>:<client> <serverlen>:<server>
 * Spaces in the client and server string are represented with
 * with backslashes.  Client and server lengths are represented in
 * ASCII decimal (which is different from the 32-bit binary we use
 * elsewhere in the replay cache).
 *
 * On parse failure, we leave the record unmodified.
 */
static krb5_error_code
check_hash_extension(krb5_donot_replay *rep)
{
    char *msghash = NULL, *client = NULL, *server = NULL, *str, *end;
    krb5_error_code retval = 0;

    /* Check if this appears to match the hash extension format. */
    if (*rep->client)
        return 0;
    if (strncmp(rep->server, "HASH:", 5) != 0)
        return 0;

    /* Parse out the message hash. */
    str = rep->server + 5;
    end = strchr(str, ' ');
    if (!end)
        return 0;
    msghash = k5memdup0(str, end - str, &retval);
    if (!msghash)
        return KRB5_RC_MALLOC;
    str = end + 1;

    /* Parse out the client and server. */
    retval = parse_counted_string(&str, &client);
    if (retval != 0 || client == NULL)
        goto error;
    if (*str != ' ')
        goto error;
    str++;
    retval = parse_counted_string(&str, &server);
    if (retval != 0 || server == NULL)
        goto error;
    if (*str)
        goto error;

    free(rep->client);
    free(rep->server);
    rep->client = client;
    rep->server = server;
    rep->msghash = msghash;
    return 0;

error:
    if (msghash)
        free(msghash);
    if (client)
        free(client);
    if (server)
        free(server);
    return retval;
}
Exemplo n.º 5
0
krb5_error_code KRB5_CALLCONV
krb5_get_fallback_host_realm(krb5_context context, krb5_data *hdata,
                             char ***realms_out)
{
    krb5_error_code ret;
    struct hostrealm_module_handle **hp;
    char **realms, *defrealm, *host, cleanname[1024];

    *realms_out = NULL;

    /* Convert hdata into a string and clean it. */
    host = k5memdup0(hdata->data, hdata->length, &ret);
    if (host == NULL)
        return ret;
    ret = k5_clean_hostname(context, host, cleanname, sizeof(cleanname));
    free(host);
    if (ret)
        return ret;

    if (context->hostrealm_handles == NULL) {
        ret = load_hostrealm_modules(context);
        if (ret)
            return ret;
    }

    /* Give each module a chance to determine the fallback realms. */
    for (hp = context->hostrealm_handles; *hp != NULL; hp++) {
        ret = fallback_realm(context, *hp, cleanname, &realms);
        if (ret == 0) {
            ret = copy_list(realms, realms_out);
            free_list(context, *hp, realms);
            return ret;
        } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
            return ret;
        }
    }

    /* Return a list containing the default realm. */
    ret = krb5_get_default_realm(context, &defrealm);
    if (ret)
        return ret;
    ret = k5_make_realmlist(defrealm, realms_out);
    krb5_free_default_realm(context, defrealm);
    return ret;
}
Exemplo n.º 6
0
Arquivo: hooks.c Projeto: INNOAUS/krb5
/* Create a fake error reply. */
static krb5_error_code
test_send_error(krb5_context context, void *data, const krb5_data *realm,
                const krb5_data *message, krb5_data **new_message_out,
                krb5_data **reply_out)
{
    krb5_error_code ret;
    krb5_error err;
    krb5_principal client, server;
    char *realm_str, *princ_str;
    int r;

    realm_str = k5memdup0(realm->data, realm->length, &ret);
    check(ret);

    r = asprintf(&princ_str, "invalid@%s", realm_str);
    assert(r > 0);
    check(krb5_parse_name(ctx, princ_str, &client));
    free(princ_str);

    r = asprintf(&princ_str, "krbtgt@%s", realm_str);
    assert(r > 0);
    check(krb5_parse_name(ctx, princ_str, &server));
    free(princ_str);
    free(realm_str);

    err.magic = KV5M_ERROR;
    err.ctime = 1971196337;
    err.cusec = 0;
    err.susec = 97008;
    err.stime = 1458219390;
    err.error = 6;
    err.client = client;
    err.server = server;
    err.text = string2data("CLIENT_NOT_FOUND");
    err.e_data = empty_data();
    check(encode_krb5_error(&err, reply_out));

    krb5_free_principal(ctx, client);
    krb5_free_principal(ctx, server);
    return 0;
}
Exemplo n.º 7
0
static krb5_error_code
get_db_opt(const char *input, char **opt_out, char **val_out)
{
    krb5_error_code ret;
    const char *pos;
    char *opt, *val = NULL;
    size_t len;

    *opt_out = *val_out = NULL;
    pos = strchr(input, '=');
    if (pos == NULL) {
        opt = strdup(input);
        if (opt == NULL)
            return ENOMEM;
    } else {
        len = pos - input;
        /* Ignore trailing spaces. */
        while (len > 0 && isspace((unsigned char)input[len - 1]))
            len--;
        opt = k5memdup0(input, len, &ret);
        if (opt == NULL)
            return ret;

        pos++;                  /* Move past '='. */
        while (isspace(*pos))   /* Ignore leading spaces. */
            pos++;
        if (*pos != '\0') {
            val = strdup(pos);
            if (val == NULL) {
                free(opt);
                return ENOMEM;
            }
        }
    }
    *opt_out = opt;
    *val_out = val;
    return 0;
}
Exemplo n.º 8
0
/* Set up conn->http.tls.  Return true on success. */
static krb5_boolean
setup_tls(krb5_context context, const krb5_data *realm,
          struct conn_state *conn, struct select_state *selstate)
{
    krb5_error_code ret;
    krb5_boolean ok = FALSE;
    char **anchors = NULL, *realmstr = NULL;
    const char *names[4];

    if (init_tls_vtable(context) != 0 || context->tls->setup == NULL)
        return FALSE;

    realmstr = k5memdup0(realm->data, realm->length, &ret);
    if (realmstr == NULL)
        goto cleanup;

    /* Load the configured anchors. */
    names[0] = KRB5_CONF_REALMS;
    names[1] = realmstr;
    names[2] = KRB5_CONF_HTTP_ANCHORS;
    names[3] = NULL;
    ret = profile_get_values(context->profile, names, &anchors);
    if (ret != 0 && ret != PROF_NO_RELATION)
        goto cleanup;

    if (context->tls->setup(context, conn->fd, conn->http.servername, anchors,
                            &conn->http.tls) != 0) {
        TRACE_SENDTO_KDC_HTTPS_ERROR_CONNECT(context, &conn->addr);
        goto cleanup;
    }

    ok = TRUE;

cleanup:
    free(realmstr);
    profile_free_list(anchors);
    return ok;
}
Exemplo n.º 9
0
/* Scan tl for a value of the given type and return it in allocated memory.
 * For KDB_TL_LINKDN, return a list of all values found. */
static krb5_error_code
decode_tl_data(krb5_tl_data *tl, int type, void **data_out)
{
    krb5_error_code ret;
    const unsigned char *ptr, *end;
    uint16_t len;
    size_t linkcount = 0, i;
    char **dnlist = NULL, **newlist;
    int *intptr;

    *data_out = NULL;

    /* Find the first matching subfield or return ENOENT.  For KDB_TL_LINKDN,
     * keep iterating after finding a match as it may be repeated. */
    ptr = tl->tl_data_contents;
    end = ptr + tl->tl_data_length;
    for (;;) {
        if (end - ptr < 3)
            break;
        len = load_16_be(ptr + 1);
        if (len > (end - ptr) - 3)
            break;
        if (*ptr != type) {
            ptr += 3 + len;
            continue;
        }
        ptr += 3;

        switch (type) {
        case KDB_TL_PRINCCOUNT:
        case KDB_TL_PRINCTYPE:
        case KDB_TL_MASK:
            if (len != 2)
                return EINVAL;
            intptr = malloc(sizeof(int));
            if (intptr == NULL)
                return ENOMEM;
            *intptr = load_16_be(ptr);
            *data_out = intptr;
            return 0;

        case KDB_TL_USERDN:
            *data_out = k5memdup0(ptr, len, &ret);
            return ret;

        case KDB_TL_LINKDN:
            newlist = realloc(dnlist, (linkcount + 2) * sizeof(char *));
            if (newlist == NULL)
                goto oom;
            dnlist = newlist;
            dnlist[linkcount] = k5memdup0(ptr, len, &ret);
            if (dnlist[linkcount] == NULL)
                goto oom;
            dnlist[linkcount + 1] = NULL;
            linkcount++;
            break;
        }

        ptr += len;
    }

    if (type != KDB_TL_LINKDN || dnlist == NULL)
        return ENOENT;
    *data_out = dnlist;
    return 0;

oom:
    for (i = 0; i < linkcount; i++)
        free(dnlist[i]);
    free(dnlist);
    return ENOMEM;
}
Exemplo n.º 10
0
/* Extract a name from policy_dn, which must be directly under the realm
 * container. */
krb5_error_code
krb5_ldap_policydn_to_name(krb5_context context, const char *policy_dn,
                           char **name_out)
{
    size_t len1, len2, plen;
    krb5_error_code ret;
    kdb5_dal_handle *dal_handle;
    krb5_ldap_context *ldap_context;
    const char *realmdn;

    *name_out = NULL;
    SETUP_CONTEXT();

    realmdn = ldap_context->lrparams->realmdn;
    if (realmdn == NULL)
        return EINVAL;

    /* policyn_dn should be "cn=<policyname>,<realmdn>". */
    len1 = strlen(realmdn);
    len2 = strlen(policy_dn);
    if (len1 == 0 || len2 == 0 || len1 + 1 >= len2)
        return EINVAL;
    plen = len2 - len1 - 1;
    if (policy_dn[plen] != ',' || strcmp(realmdn, policy_dn + plen + 1) != 0)
        return EINVAL;

#if defined HAVE_LDAP_STR2DN
    {
        char *rdn;
        LDAPDN dn;

        rdn = k5memdup0(policy_dn, plen, &ret);
        if (rdn == NULL)
            return ret;
        ret = ldap_str2dn(rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC);
        free(rdn);
        if (ret)
            return EINVAL;
        if (dn[0] == NULL || dn[1] != NULL ||
            dn[0][0]->la_attr.bv_len != 2 ||
            strncasecmp(dn[0][0]->la_attr.bv_val, "cn", 2) != 0) {
            ret = EINVAL;
        } else {
            *name_out = k5memdup0(dn[0][0]->la_value.bv_val,
                                  dn[0][0]->la_value.bv_len, &ret);
        }
        ldap_dnfree(dn);
        return ret;
    }
#elif defined HAVE_LDAP_EXPLODE_DN
    {
        char **parsed_dn;

        /* 1 = return DN components without type prefix */
        parsed_dn = ldap_explode_dn(policy_dn, 1);
        if (parsed_dn == NULL)
            return EINVAL;
        *name_out = strdup(parsed_dn[0]);
        if (*name_out == NULL)
            return ENOMEM;
        ldap_value_free(parsed_dn);
        return 0;
    }
#else
    return EINVAL;
#endif
}
Exemplo n.º 11
0
/* Used by the slave to update its hash db from* the incr update log.  Must be
 * called with lock held. */
krb5_error_code
ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret, char **db_args)
{
    krb5_db_entry *entry = NULL;
    kdb_incr_update_t *upd = NULL, *fupd;
    int i, no_of_updates;
    krb5_error_code retval;
    krb5_principal dbprinc;
    kdb_last_t errlast, *last;
    char *dbprincstr;
    kdb_log_context *log_ctx;
    kdb_hlog_t *ulog = NULL;

    INIT_ULOG(context);

    no_of_updates = incr_ret->updates.kdb_ulog_t_len;
    upd = incr_ret->updates.kdb_ulog_t_val;
    fupd = upd;

    /* We reset last_sno and last_time to 0, if krb5_db2_db_put_principal or
     * krb5_db2_db_delete_principal fail. */
    errlast.last_sno = (unsigned int)0;
    errlast.last_time.seconds = (unsigned int)0;
    errlast.last_time.useconds = (unsigned int)0;
    last = &errlast;

    retval = krb5_db_open(context, db_args,
                          KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
    if (retval)
        goto cleanup;

    for (i = 0; i < no_of_updates; i++) {
        if (!upd->kdb_commit)
            continue;

        if (upd->kdb_deleted) {
            dbprincstr = k5memdup0(upd->kdb_princ_name.utf8str_t_val,
                                   upd->kdb_princ_name.utf8str_t_len, &retval);
            if (dbprincstr == NULL)
                goto cleanup;

            retval = krb5_parse_name(context, dbprincstr, &dbprinc);
            free(dbprincstr);
            if (retval)
                goto cleanup;

            retval = krb5int_delete_principal_no_log(context, dbprinc);
            krb5_free_principal(context, dbprinc);
            if (retval)
                goto cleanup;
        } else {
            entry = k5alloc(sizeof(krb5_db_entry), &retval);
            if (entry == NULL)
                goto cleanup;

            retval = ulog_conv_2dbentry(context, &entry, upd);
            if (retval)
                goto cleanup;

            retval = krb5int_put_principal_no_log(context, entry);
            krb5_db_free_principal(context, entry);
            if (retval)
                goto cleanup;
        }

        upd++;
    }

    last = &incr_ret->lastentry;

cleanup:
    if (fupd)
        ulog_free_entries(fupd, no_of_updates);

    /* Record a new last serial number and timestamp in the ulog header. */
    ulog->kdb_last_sno = last->last_sno;
    ulog->kdb_last_time = last->last_time;
    ulog_sync_header(ulog);

    return retval;
}
Exemplo n.º 12
0
static krb5_error_code
module_locate_server(krb5_context ctx, const krb5_data *realm,
                     struct serverlist *serverlist,
                     enum locate_service_type svc, int socktype)
{
    struct krb5plugin_service_locate_result *res = NULL;
    krb5_error_code code;
    struct krb5plugin_service_locate_ftable *vtbl = NULL;
    void **ptrs;
    char *realmz;               /* NUL-terminated realm */
    int i;
    struct module_callback_data cbdata = { 0, };
    const char *msg;

    Tprintf("in module_locate_server\n");
    cbdata.list = serverlist;
    if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) {

        code = krb5int_open_plugin_dirs (objdirs, NULL, &ctx->libkrb5_plugins,
                                         &ctx->err);
        if (code)
            return KRB5_PLUGIN_NO_HANDLE;
    }

    code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins,
                                        "service_locator", &ptrs, &ctx->err);
    if (code) {
        Tprintf("error looking up plugin symbols: %s\n",
                (msg = krb5_get_error_message(ctx, code)));
        krb5_free_error_message(ctx, msg);
        return KRB5_PLUGIN_NO_HANDLE;
    }

    if (realm->length >= UINT_MAX) {
        krb5int_free_plugin_dir_data(ptrs);
        return ENOMEM;
    }
    realmz = k5memdup0(realm->data, realm->length, &code);
    if (realmz == NULL) {
        krb5int_free_plugin_dir_data(ptrs);
        return code;
    }
    for (i = 0; ptrs[i]; i++) {
        void *blob;

        vtbl = ptrs[i];
        Tprintf("element %d is %p\n", i, ptrs[i]);

        /* For now, don't keep the plugin data alive.  For long-lived
           contexts, it may be desirable to change that later.  */
        code = vtbl->init(ctx, &blob);
        if (code)
            continue;

        code = vtbl->lookup(blob, svc, realmz,
                            (socktype != 0) ? socktype : SOCK_DGRAM, AF_UNSPEC,
                            module_callback, &cbdata);
        /* Also ask for TCP addresses if we got UDP addresses and want both. */
        if (code == 0 && socktype == 0) {
            code = vtbl->lookup(blob, svc, realmz, SOCK_STREAM, AF_UNSPEC,
                                module_callback, &cbdata);
            if (code == KRB5_PLUGIN_NO_HANDLE)
                code = 0;
        }
        vtbl->fini(blob);
        if (code == KRB5_PLUGIN_NO_HANDLE) {
            /* Module passes, keep going.  */
            /* XXX */
            Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n");
            continue;
        }
        if (code != 0) {
            /* Module encountered an actual error.  */
            Tprintf("plugin lookup routine returned error %d: %s\n",
                    code, error_message(code));
            free(realmz);
            krb5int_free_plugin_dir_data (ptrs);
            return code;
        }
        break;
    }
    if (ptrs[i] == NULL) {
        Tprintf("ran off end of plugin list\n");
        free(realmz);
        krb5int_free_plugin_dir_data (ptrs);
        return KRB5_PLUGIN_NO_HANDLE;
    }
    Tprintf("stopped with plugin #%d, res=%p\n", i, res);

    /* Got something back, yippee.  */
    Tprintf("now have %lu addrs in list %p\n",
            (unsigned long) serverlist->nservers, serverlist);
    free(realmz);
    krb5int_free_plugin_dir_data (ptrs);
    return 0;
}
Exemplo n.º 13
0
static krb5_error_code
locate_srv_conf_1(krb5_context context, const krb5_data *realm,
                  const char * name, struct serverlist *serverlist,
                  k5_transport transport, int udpport)
{
    const char *realm_srv_names[4];
    char **hostlist = NULL, *realmstr = NULL, *host = NULL;
    const char *hostspec;
    krb5_error_code code;
    int i, default_port;

    Tprintf("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
            realm->data, name, udpport);

    realmstr = k5memdup0(realm->data, realm->length, &code);
    if (realmstr == NULL)
        goto cleanup;

    realm_srv_names[0] = KRB5_CONF_REALMS;
    realm_srv_names[1] = realmstr;
    realm_srv_names[2] = name;
    realm_srv_names[3] = 0;
    code = profile_get_values(context->profile, realm_srv_names, &hostlist);
    if (code) {
        Tprintf("config file lookup failed: %s\n", error_message(code));
        if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
            code = 0;
        goto cleanup;
    }

    for (i = 0; hostlist[i]; i++) {
        int port_num;
        k5_transport this_transport = transport;
        const char *uri_path = NULL;

        hostspec = hostlist[i];
        Tprintf("entry %d is '%s'\n", i, hostspec);

        parse_uri_if_https(hostspec, &this_transport, &hostspec, &uri_path);

        default_port = (this_transport == HTTPS) ? 443 : udpport;
        code = k5_parse_host_string(hostspec, default_port, &host, &port_num);
        if (code == 0 && host == NULL)
            code = EINVAL;
        if (code)
            goto cleanup;

        code = add_host_to_list(serverlist, host, port_num, this_transport,
                                AF_UNSPEC, uri_path, -1);
        if (code)
            goto cleanup;

        free(host);
        host = NULL;
    }

cleanup:
    free(realmstr);
    free(host);
    profile_free_list(hostlist);
    return code;
}
Exemplo n.º 14
0
/* Used by the slave to update its hash db from the incr update log. */
krb5_error_code
ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret, char **db_args)
{
    krb5_db_entry *entry = NULL;
    kdb_incr_update_t *upd = NULL, *fupd;
    int i, no_of_updates;
    krb5_error_code retval;
    krb5_principal dbprinc;
    char *dbprincstr;
    kdb_log_context *log_ctx;
    kdb_hlog_t *ulog = NULL;

    INIT_ULOG(context);

    /* Lock the DB before the ulog to avoid deadlock. */
    retval = krb5_db_open(context, db_args,
                          KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
    if (retval)
        return retval;
    retval = krb5_db_lock(context, KRB5_DB_LOCKMODE_EXCLUSIVE);
    if (retval)
        return retval;
    retval = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE);
    if (retval) {
        krb5_db_unlock(context);
        return retval;
    }

    no_of_updates = incr_ret->updates.kdb_ulog_t_len;
    upd = incr_ret->updates.kdb_ulog_t_val;
    fupd = upd;

    for (i = 0; i < no_of_updates; i++) {
        if (!upd->kdb_commit)
            continue;

        /* If (unexpectedly) this update does not follow the last one we
         * stored, discard any previous ulog state. */
        if (ulog->kdb_num != 0 && upd->kdb_entry_sno != ulog->kdb_last_sno + 1)
            reset_header(ulog);

        if (upd->kdb_deleted) {
            dbprincstr = k5memdup0(upd->kdb_princ_name.utf8str_t_val,
                                   upd->kdb_princ_name.utf8str_t_len, &retval);
            if (dbprincstr == NULL)
                goto cleanup;

            retval = krb5_parse_name(context, dbprincstr, &dbprinc);
            free(dbprincstr);
            if (retval)
                goto cleanup;

            retval = krb5int_delete_principal_no_log(context, dbprinc);
            krb5_free_principal(context, dbprinc);
            if (retval == KRB5_KDB_NOENTRY)
                retval = 0;
            if (retval)
                goto cleanup;
        } else {
            entry = k5alloc(sizeof(krb5_db_entry), &retval);
            if (entry == NULL)
                goto cleanup;

            retval = ulog_conv_2dbentry(context, &entry, upd);
            if (retval)
                goto cleanup;

            retval = krb5int_put_principal_no_log(context, entry);
            krb5_db_free_principal(context, entry);
            if (retval)
                goto cleanup;
        }

        retval = store_update(log_ctx, upd);
        if (retval)
            goto cleanup;

        upd++;
    }

cleanup:
    if (fupd)
        ulog_free_entries(fupd, no_of_updates);
    if (retval) {
        reset_header(ulog);
        sync_header(ulog);
    }
    unlock_ulog(context);
    krb5_db_unlock(context);
    return retval;
}
Exemplo n.º 15
0
static krb5_error_code
process_chpw_request(krb5_context context, void *server_handle, char *realm,
                     krb5_keytab keytab, const krb5_fulladdr *local_faddr,
                     const krb5_fulladdr *remote_faddr, krb5_data *req,
                     krb5_data *rep)
{
    krb5_error_code ret;
    char *ptr;
    unsigned int plen, vno;
    krb5_data ap_req, ap_rep = empty_data();
    krb5_data cipher = empty_data(), clear = empty_data();
    krb5_auth_context auth_context = NULL;
    krb5_principal changepw = NULL;
    krb5_principal client, target = NULL;
    krb5_ticket *ticket = NULL;
    krb5_replay_data replay;
    krb5_error krberror;
    int numresult;
    char strresult[1024];
    char *clientstr = NULL, *targetstr = NULL;
    const char *errmsg = NULL;
    size_t clen;
    char *cdots;
    struct sockaddr_storage ss;
    socklen_t salen;
    char addrbuf[100];
    krb5_address *addr = remote_faddr->address;

    *rep = empty_data();

    if (req->length < 4) {
        /* either this, or the server is printing bad messages,
           or the caller passed in garbage */
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request was truncated", sizeof(strresult));
        goto bailout;
    }

    ptr = req->data;

    /* verify length */

    plen = (*ptr++ & 0xff);
    plen = (plen<<8) | (*ptr++ & 0xff);

    if (plen != req->length) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request length was inconsistent",
                sizeof(strresult));
        goto bailout;
    }

    /* verify version number */

    vno = (*ptr++ & 0xff) ;
    vno = (vno<<8) | (*ptr++ & 0xff);

    if (vno != 1 && vno != RFC3244_VERSION) {
        ret = KRB5KDC_ERR_BAD_PVNO;
        numresult = KRB5_KPASSWD_BAD_VERSION;
        snprintf(strresult, sizeof(strresult),
                 "Request contained unknown protocol version number %d", vno);
        goto bailout;
    }

    /* read, check ap-req length */

    ap_req.length = (*ptr++ & 0xff);
    ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);

    if (ptr + ap_req.length >= req->data + req->length) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request was truncated in AP-REQ",
                sizeof(strresult));
        goto bailout;
    }

    /* verify ap_req */

    ap_req.data = ptr;
    ptr += ap_req.length;

    ret = krb5_auth_con_init(context, &auth_context);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed initializing auth context",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_auth_con_setflags(context, auth_context,
                                 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed initializing auth context",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
                               "kadmin", "changepw", NULL);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed building kadmin/changepw principal",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
                      NULL, &ticket);

    if (ret) {
        numresult = KRB5_KPASSWD_AUTHERROR;
        strlcpy(strresult, "Failed reading application request",
                sizeof(strresult));
        goto chpwfail;
    }

    /* construct the ap-rep */

    ret = krb5_mk_rep(context, auth_context, &ap_rep);
    if (ret) {
        numresult = KRB5_KPASSWD_AUTHERROR;
        strlcpy(strresult, "Failed replying to application request",
                sizeof(strresult));
        goto chpwfail;
    }

    /* decrypt the ChangePasswdData */

    cipher.length = (req->data + req->length) - ptr;
    cipher.data = ptr;

    /*
     * Don't set a remote address in auth_context before calling krb5_rd_priv,
     * so that we can work against clients behind a NAT.  Reflection attacks
     * aren't a concern since we use sequence numbers and since our requests
     * don't look anything like our responses.  Also don't set a local address,
     * since we don't know what interface the request was received on.
     */

    ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed decrypting request", sizeof(strresult));
        goto chpwfail;
    }

    client = ticket->enc_part2->client;

    /* decode ChangePasswdData for setpw requests */
    if (vno == RFC3244_VERSION) {
        krb5_data *clear_data;

        ret = decode_krb5_setpw_req(&clear, &clear_data, &target);
        if (ret != 0) {
            numresult = KRB5_KPASSWD_MALFORMED;
            strlcpy(strresult, "Failed decoding ChangePasswdData",
                    sizeof(strresult));
            goto chpwfail;
        }

        zapfree(clear.data, clear.length);

        clear = *clear_data;
        free(clear_data);

        if (target != NULL) {
            ret = krb5_unparse_name(context, target, &targetstr);
            if (ret != 0) {
                numresult = KRB5_KPASSWD_HARDERROR;
                strlcpy(strresult, "Failed unparsing target name for log",
                        sizeof(strresult));
                goto chpwfail;
            }
        }
    }

    ret = krb5_unparse_name(context, client, &clientstr);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed unparsing client name for log",
                sizeof(strresult));
        goto chpwfail;
    }

    /* for cpw, verify that this is an AS_REQ ticket */
    if (vno == 1 &&
        (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) {
        numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
        strlcpy(strresult, "Ticket must be derived from a password",
                sizeof(strresult));
        goto chpwfail;
    }

    /* change the password */

    ptr = k5memdup0(clear.data, clear.length, &ret);
    ret = schpw_util_wrapper(server_handle, client, target,
                             (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0,
                             ptr, NULL, strresult, sizeof(strresult));
    if (ret)
        errmsg = krb5_get_error_message(context, ret);

    /* zap the password */
    zapfree(clear.data, clear.length);
    zapfree(ptr, clear.length);
    clear = empty_data();

    clen = strlen(clientstr);
    trunc_name(&clen, &cdots);

    switch (addr->addrtype) {
    case ADDRTYPE_INET: {
        struct sockaddr_in *sin = ss2sin(&ss);

        sin->sin_family = AF_INET;
        memcpy(&sin->sin_addr, addr->contents, addr->length);
        sin->sin_port = htons(remote_faddr->port);
        salen = sizeof(*sin);
        break;
    }
    case ADDRTYPE_INET6: {
        struct sockaddr_in6 *sin6 = ss2sin6(&ss);

        sin6->sin6_family = AF_INET6;
        memcpy(&sin6->sin6_addr, addr->contents, addr->length);
        sin6->sin6_port = htons(remote_faddr->port);
        salen = sizeof(*sin6);
        break;
    }
    default: {
        struct sockaddr *sa = ss2sa(&ss);

        sa->sa_family = AF_UNSPEC;
        salen = sizeof(*sa);
        break;
    }
    }

    if (getnameinfo(ss2sa(&ss), salen,
                    addrbuf, sizeof(addrbuf), NULL, 0,
                    NI_NUMERICHOST | NI_NUMERICSERV) != 0)
        strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf));

    if (vno == RFC3244_VERSION) {
        size_t tlen;
        char *tdots;
        const char *targetp;

        if (target == NULL) {
            tlen = clen;
            tdots = cdots;
            targetp = targetstr;
        } else {
            tlen = strlen(targetstr);
            trunc_name(&tlen, &tdots);
            targetp = clientstr;
        }

        krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for "
                                       "%.*s%s: %s"), addrbuf, (int) clen,
                         clientstr, cdots, (int) tlen, targetp, tdots,
                         errmsg ? errmsg : "success");
    } else {
        krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"),
                         addrbuf, (int) clen, clientstr, cdots,
                         errmsg ? errmsg : "success");
    }
    switch (ret) {
    case KADM5_AUTH_CHANGEPW:
        numresult = KRB5_KPASSWD_ACCESSDENIED;
        break;
    case KADM5_PASS_Q_TOOSHORT:
    case KADM5_PASS_REUSE:
    case KADM5_PASS_Q_CLASS:
    case KADM5_PASS_Q_DICT:
    case KADM5_PASS_Q_GENERIC:
    case KADM5_PASS_TOOSOON:
        numresult = KRB5_KPASSWD_SOFTERROR;
        break;
    case 0:
        numresult = KRB5_KPASSWD_SUCCESS;
        strlcpy(strresult, "", sizeof(strresult));
        break;
    default:
        numresult = KRB5_KPASSWD_HARDERROR;
        break;
    }

chpwfail:

    ret = alloc_data(&clear, 2 + strlen(strresult));
    if (ret)
        goto bailout;

    ptr = clear.data;

    *ptr++ = (numresult>>8) & 0xff;
    *ptr++ = numresult & 0xff;

    memcpy(ptr, strresult, strlen(strresult));

    cipher = empty_data();

    if (ap_rep.length) {
        ret = krb5_auth_con_setaddrs(context, auth_context,
                                     local_faddr->address, NULL);
        if (ret) {
            numresult = KRB5_KPASSWD_HARDERROR;
            strlcpy(strresult,
                    "Failed storing client and server internet addresses",
                    sizeof(strresult));
        } else {
            ret = krb5_mk_priv(context, auth_context, &clear, &cipher,
                               &replay);
            if (ret) {
                numresult = KRB5_KPASSWD_HARDERROR;
                strlcpy(strresult, "Failed encrypting reply",
                        sizeof(strresult));
            }
        }
    }

    /* if no KRB-PRIV was constructed, then we need a KRB-ERROR.
       if this fails, just bail.  there's nothing else we can do. */

    if (cipher.length == 0) {
        /* clear out ap_rep now, so that it won't be inserted in the
           reply */

        if (ap_rep.length) {
            free(ap_rep.data);
            ap_rep = empty_data();
        }

        krberror.ctime = 0;
        krberror.cusec = 0;
        krberror.susec = 0;
        ret = krb5_timeofday(context, &krberror.stime);
        if (ret)
            goto bailout;

        /* this is really icky.  but it's what all the other callers
           to mk_error do. */
        krberror.error = ret;
        krberror.error -= ERROR_TABLE_BASE_krb5;
        if (krberror.error < 0 || krberror.error > KRB_ERR_MAX)
            krberror.error = KRB_ERR_GENERIC;

        krberror.client = NULL;

        ret = krb5_build_principal(context, &krberror.server,
                                   strlen(realm), realm,
                                   "kadmin", "changepw", NULL);
        if (ret)
            goto bailout;
        krberror.text.length = 0;
        krberror.e_data = clear;

        ret = krb5_mk_error(context, &krberror, &cipher);

        krb5_free_principal(context, krberror.server);

        if (ret)
            goto bailout;
    }

    /* construct the reply */

    ret = alloc_data(rep, 6 + ap_rep.length + cipher.length);
    if (ret)
        goto bailout;
    ptr = rep->data;

    /* length */

    *ptr++ = (rep->length>>8) & 0xff;
    *ptr++ = rep->length & 0xff;

    /* version == 0x0001 big-endian */

    *ptr++ = 0;
    *ptr++ = 1;

    /* ap_rep length, big-endian */

    *ptr++ = (ap_rep.length>>8) & 0xff;
    *ptr++ = ap_rep.length & 0xff;

    /* ap-rep data */

    if (ap_rep.length) {
        memcpy(ptr, ap_rep.data, ap_rep.length);
        ptr += ap_rep.length;
    }

    /* krb-priv or krb-error */

    memcpy(ptr, cipher.data, cipher.length);

bailout:
    krb5_auth_con_free(context, auth_context);
    krb5_free_principal(context, changepw);
    krb5_free_ticket(context, ticket);
    free(ap_rep.data);
    free(clear.data);
    free(cipher.data);
    krb5_free_principal(context, target);
    krb5_free_unparsed_name(context, targetstr);
    krb5_free_unparsed_name(context, clientstr);
    krb5_free_error_message(context, errmsg);
    return ret;
}