Path: blob/main/crypto/heimdal/lib/kadm5/init_c.c
105688 views
/*1* Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* 3. Neither the name of the Institute nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#include "kadm5_locl.h"34#include <sys/types.h>35#ifdef HAVE_SYS_SOCKET_H36#include <sys/socket.h>37#endif38#ifdef HAVE_NETINET_IN_H39#include <netinet/in.h>40#endif41#ifdef HAVE_NETDB_H42#include <netdb.h>43#endif4445RCSID("$Id$");4647static void48set_funcs(kadm5_client_context *c)49{50#define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F51SET(c, chpass_principal);52SET(c, chpass_principal_with_key);53SET(c, create_principal);54SET(c, delete_principal);55SET(c, destroy);56SET(c, flush);57SET(c, get_principal);58SET(c, get_principals);59SET(c, get_privs);60SET(c, modify_principal);61SET(c, randkey_principal);62SET(c, rename_principal);63}6465kadm5_ret_t66_kadm5_c_init_context(kadm5_client_context **ctx,67kadm5_config_params *params,68krb5_context context)69{70krb5_error_code ret;71char *colon;7273*ctx = malloc(sizeof(**ctx));74if(*ctx == NULL)75return ENOMEM;76memset(*ctx, 0, sizeof(**ctx));77krb5_add_et_list (context, initialize_kadm5_error_table_r);78set_funcs(*ctx);79(*ctx)->context = context;80if(params->mask & KADM5_CONFIG_REALM) {81ret = 0;82(*ctx)->realm = strdup(params->realm);83if ((*ctx)->realm == NULL)84ret = ENOMEM;85} else86ret = krb5_get_default_realm((*ctx)->context, &(*ctx)->realm);87if (ret) {88free(*ctx);89return ret;90}91if(params->mask & KADM5_CONFIG_ADMIN_SERVER)92(*ctx)->admin_server = strdup(params->admin_server);93else {94char **hostlist;9596ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist);97if (ret) {98free((*ctx)->realm);99free(*ctx);100return ret;101}102(*ctx)->admin_server = strdup(*hostlist);103krb5_free_krbhst (context, hostlist);104}105106if ((*ctx)->admin_server == NULL) {107free((*ctx)->realm);108free(*ctx);109return ENOMEM;110}111colon = strchr ((*ctx)->admin_server, ':');112if (colon != NULL)113*colon++ = '\0';114115(*ctx)->kadmind_port = 0;116117if(params->mask & KADM5_CONFIG_KADMIND_PORT)118(*ctx)->kadmind_port = params->kadmind_port;119else if (colon != NULL) {120char *end;121122(*ctx)->kadmind_port = htons(strtol (colon, &end, 0));123}124if ((*ctx)->kadmind_port == 0)125(*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm",126"tcp", 749);127return 0;128}129130static krb5_error_code131get_kadm_ticket(krb5_context context,132krb5_ccache id,133krb5_principal client,134const char *server_name)135{136krb5_error_code ret;137krb5_creds in, *out;138139memset(&in, 0, sizeof(in));140in.client = client;141ret = krb5_parse_name(context, server_name, &in.server);142if(ret)143return ret;144ret = krb5_get_credentials(context, 0, id, &in, &out);145if(ret == 0)146krb5_free_creds(context, out);147krb5_free_principal(context, in.server);148return ret;149}150151static krb5_error_code152get_new_cache(krb5_context context,153krb5_principal client,154const char *password,155krb5_prompter_fct prompter,156const char *keytab,157const char *server_name,158krb5_ccache *ret_cache)159{160krb5_error_code ret;161krb5_creds cred;162krb5_get_init_creds_opt *opt;163krb5_ccache id;164165ret = krb5_get_init_creds_opt_alloc (context, &opt);166if (ret)167return ret;168169krb5_get_init_creds_opt_set_default_flags(context, "kadmin",170krb5_principal_get_realm(context,171client),172opt);173174175krb5_get_init_creds_opt_set_forwardable (opt, FALSE);176krb5_get_init_creds_opt_set_proxiable (opt, FALSE);177178if(password == NULL && prompter == NULL) {179krb5_keytab kt;180if(keytab == NULL)181ret = krb5_kt_default(context, &kt);182else183ret = krb5_kt_resolve(context, keytab, &kt);184if(ret) {185krb5_get_init_creds_opt_free(context, opt);186return ret;187}188ret = krb5_get_init_creds_keytab (context,189&cred,190client,191kt,1920,193server_name,194opt);195krb5_kt_close(context, kt);196} else {197ret = krb5_get_init_creds_password (context,198&cred,199client,200password,201prompter,202NULL,2030,204server_name,205opt);206}207krb5_get_init_creds_opt_free(context, opt);208switch(ret){209case 0:210break;211case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */212case KRB5KRB_AP_ERR_BAD_INTEGRITY:213case KRB5KRB_AP_ERR_MODIFIED:214return KADM5_BAD_PASSWORD;215default:216return ret;217}218ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);219if(ret)220return ret;221ret = krb5_cc_initialize (context, id, cred.client);222if (ret)223return ret;224ret = krb5_cc_store_cred (context, id, &cred);225if (ret)226return ret;227krb5_free_cred_contents (context, &cred);228*ret_cache = id;229return 0;230}231232/*233* Check the credential cache `id´ to figure out what principal to use234* when talking to the kadmind. If there is a initial kadmin/admin@235* credential in the cache, use that client principal. Otherwise, use236* the client principals first component and add /admin to the237* principal.238*/239240static krb5_error_code241get_cache_principal(krb5_context context,242krb5_ccache *id,243krb5_principal *client)244{245krb5_error_code ret;246const char *name, *inst;247krb5_principal p1, p2;248249ret = krb5_cc_default(context, id);250if(ret) {251*id = NULL;252return ret;253}254255ret = krb5_cc_get_principal(context, *id, &p1);256if(ret) {257krb5_cc_close(context, *id);258*id = NULL;259return ret;260}261262ret = krb5_make_principal(context, &p2, NULL,263"kadmin", "admin", NULL);264if (ret) {265krb5_cc_close(context, *id);266*id = NULL;267krb5_free_principal(context, p1);268return ret;269}270271{272krb5_creds in, *out;273krb5_kdc_flags flags;274275flags.i = 0;276memset(&in, 0, sizeof(in));277278in.client = p1;279in.server = p2;280281/* check for initial ticket kadmin/admin */282ret = krb5_get_credentials_with_flags(context, KRB5_GC_CACHED, flags,283*id, &in, &out);284krb5_free_principal(context, p2);285if (ret == 0) {286if (out->flags.b.initial) {287*client = p1;288krb5_free_creds(context, out);289return 0;290}291krb5_free_creds(context, out);292}293}294krb5_cc_close(context, *id);295*id = NULL;296297name = krb5_principal_get_comp_string(context, p1, 0);298inst = krb5_principal_get_comp_string(context, p1, 1);299if(inst == NULL || strcmp(inst, "admin") != 0) {300ret = krb5_make_principal(context, &p2, NULL, name, "admin", NULL);301krb5_free_principal(context, p1);302if(ret != 0)303return ret;304305*client = p2;306return 0;307}308309*client = p1;310311return 0;312}313314krb5_error_code315_kadm5_c_get_cred_cache(krb5_context context,316const char *client_name,317const char *server_name,318const char *password,319krb5_prompter_fct prompter,320const char *keytab,321krb5_ccache ccache,322krb5_ccache *ret_cache)323{324krb5_error_code ret;325krb5_ccache id = NULL;326krb5_principal default_client = NULL, client = NULL;327328/* treat empty password as NULL */329if(password && *password == '\0')330password = NULL;331if(server_name == NULL)332server_name = KADM5_ADMIN_SERVICE;333334if(client_name != NULL) {335ret = krb5_parse_name(context, client_name, &client);336if(ret)337return ret;338}339340if(ccache != NULL) {341id = ccache;342ret = krb5_cc_get_principal(context, id, &client);343if(ret)344return ret;345} else {346/* get principal from default cache, ok if this doesn't work */347348ret = get_cache_principal(context, &id, &default_client);349if (ret) {350/*351* No client was specified by the caller and we cannot352* determine the client from a credentials cache.353*/354const char *user;355356user = get_default_username ();357358if(user == NULL) {359krb5_set_error_message(context, KADM5_FAILURE, "Unable to find local user name");360return KADM5_FAILURE;361}362ret = krb5_make_principal(context, &default_client,363NULL, user, "admin", NULL);364if(ret)365return ret;366}367}368369370/*371* No client was specified by the caller, but we have a client372* from the default credentials cache.373*/374if (client == NULL && default_client != NULL)375client = default_client;376377378if(id && client && (default_client == NULL ||379krb5_principal_compare(context, client, default_client) != 0)) {380ret = get_kadm_ticket(context, id, client, server_name);381if(ret == 0) {382*ret_cache = id;383krb5_free_principal(context, default_client);384if (default_client != client)385krb5_free_principal(context, client);386return 0;387}388if(ccache != NULL)389/* couldn't get ticket from cache */390return -1;391}392/* get creds via AS request */393if(id && (id != ccache))394krb5_cc_close(context, id);395if (client != default_client)396krb5_free_principal(context, default_client);397398ret = get_new_cache(context, client, password, prompter, keytab,399server_name, ret_cache);400krb5_free_principal(context, client);401return ret;402}403404static kadm5_ret_t405kadm_connect(kadm5_client_context *ctx)406{407kadm5_ret_t ret;408krb5_principal server;409krb5_ccache cc;410rk_socket_t s = rk_INVALID_SOCKET;411struct addrinfo *ai, *a;412struct addrinfo hints;413int error;414char portstr[NI_MAXSERV];415char *hostname, *slash;416char *service_name;417krb5_context context = ctx->context;418419memset (&hints, 0, sizeof(hints));420hints.ai_socktype = SOCK_STREAM;421hints.ai_protocol = IPPROTO_TCP;422423snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port));424425hostname = ctx->admin_server;426slash = strchr (hostname, '/');427if (slash != NULL)428hostname = slash + 1;429430error = getaddrinfo (hostname, portstr, &hints, &ai);431if (error) {432krb5_clear_error_message(context);433return KADM5_BAD_SERVER_NAME;434}435436for (a = ai; a != NULL; a = a->ai_next) {437s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);438if (s < 0)439continue;440if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {441krb5_clear_error_message(context);442krb5_warn (context, errno, "connect(%s)", hostname);443rk_closesocket (s);444continue;445}446break;447}448if (a == NULL) {449freeaddrinfo (ai);450krb5_clear_error_message(context);451krb5_warnx (context, "failed to contact %s", hostname);452return KADM5_FAILURE;453}454ret = _kadm5_c_get_cred_cache(context,455ctx->client_name,456ctx->service_name,457NULL, ctx->prompter, ctx->keytab,458ctx->ccache, &cc);459460if(ret) {461freeaddrinfo (ai);462rk_closesocket(s);463return ret;464}465466if (ctx->realm)467asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm);468else469asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE);470471if (service_name == NULL) {472freeaddrinfo (ai);473rk_closesocket(s);474krb5_clear_error_message(context);475return ENOMEM;476}477478ret = krb5_parse_name(context, service_name, &server);479free(service_name);480if(ret) {481freeaddrinfo (ai);482if(ctx->ccache == NULL)483krb5_cc_close(context, cc);484rk_closesocket(s);485return ret;486}487ctx->ac = NULL;488489ret = krb5_sendauth(context, &ctx->ac, &s,490KADMIN_APPL_VERSION, NULL,491server, AP_OPTS_MUTUAL_REQUIRED,492NULL, NULL, cc, NULL, NULL, NULL);493if(ret == 0) {494krb5_data params;495kadm5_config_params p;496memset(&p, 0, sizeof(p));497if(ctx->realm) {498p.mask |= KADM5_CONFIG_REALM;499p.realm = ctx->realm;500}501ret = _kadm5_marshal_params(context, &p, ¶ms);502503ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms);504krb5_data_free(¶ms);505if(ret) {506freeaddrinfo (ai);507rk_closesocket(s);508if(ctx->ccache == NULL)509krb5_cc_close(context, cc);510return ret;511}512} else if(ret == KRB5_SENDAUTH_BADAPPLVERS) {513rk_closesocket(s);514515s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);516if (s < 0) {517freeaddrinfo (ai);518krb5_clear_error_message(context);519return errno;520}521if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {522rk_closesocket (s);523freeaddrinfo (ai);524krb5_clear_error_message(context);525return errno;526}527ret = krb5_sendauth(context, &ctx->ac, &s,528KADMIN_OLD_APPL_VERSION, NULL,529server, AP_OPTS_MUTUAL_REQUIRED,530NULL, NULL, cc, NULL, NULL, NULL);531}532freeaddrinfo (ai);533if(ret) {534rk_closesocket(s);535return ret;536}537538krb5_free_principal(context, server);539if(ctx->ccache == NULL)540krb5_cc_close(context, cc);541ctx->sock = s;542543return 0;544}545546kadm5_ret_t547_kadm5_connect(void *handle)548{549kadm5_client_context *ctx = handle;550if(ctx->sock == -1)551return kadm_connect(ctx);552return 0;553}554555static kadm5_ret_t556kadm5_c_init_with_context(krb5_context context,557const char *client_name,558const char *password,559krb5_prompter_fct prompter,560const char *keytab,561krb5_ccache ccache,562const char *service_name,563kadm5_config_params *realm_params,564unsigned long struct_version,565unsigned long api_version,566void **server_handle)567{568kadm5_ret_t ret;569kadm5_client_context *ctx = NULL;570krb5_ccache cc;571572ret = _kadm5_c_init_context(&ctx, realm_params, context);573if(ret)574return ret;575576if(password != NULL && *password != '\0') {577ret = _kadm5_c_get_cred_cache(context,578client_name,579service_name,580password, prompter, keytab, ccache, &cc);581if(ret)582return ret; /* XXX */583ccache = cc;584}585586587if (client_name != NULL)588ctx->client_name = strdup(client_name);589else590ctx->client_name = NULL;591if (service_name != NULL)592ctx->service_name = strdup(service_name);593else594ctx->service_name = NULL;595ctx->prompter = prompter;596ctx->keytab = keytab;597ctx->ccache = ccache;598/* maybe we should copy the params here */599ctx->sock = -1;600601*server_handle = ctx;602return 0;603}604605static kadm5_ret_t606init_context(const char *client_name,607const char *password,608krb5_prompter_fct prompter,609const char *keytab,610krb5_ccache ccache,611const char *service_name,612kadm5_config_params *realm_params,613unsigned long struct_version,614unsigned long api_version,615void **server_handle)616{617krb5_context context;618kadm5_ret_t ret;619kadm5_server_context *ctx;620621ret = krb5_init_context(&context);622if (ret)623return ret;624ret = kadm5_c_init_with_context(context,625client_name,626password,627prompter,628keytab,629ccache,630service_name,631realm_params,632struct_version,633api_version,634server_handle);635if(ret){636krb5_free_context(context);637return ret;638}639ctx = *server_handle;640ctx->my_context = 1;641return 0;642}643644kadm5_ret_t645kadm5_c_init_with_password_ctx(krb5_context context,646const char *client_name,647const char *password,648const char *service_name,649kadm5_config_params *realm_params,650unsigned long struct_version,651unsigned long api_version,652void **server_handle)653{654return kadm5_c_init_with_context(context,655client_name,656password,657krb5_prompter_posix,658NULL,659NULL,660service_name,661realm_params,662struct_version,663api_version,664server_handle);665}666667kadm5_ret_t668kadm5_c_init_with_password(const char *client_name,669const char *password,670const char *service_name,671kadm5_config_params *realm_params,672unsigned long struct_version,673unsigned long api_version,674void **server_handle)675{676return init_context(client_name,677password,678krb5_prompter_posix,679NULL,680NULL,681service_name,682realm_params,683struct_version,684api_version,685server_handle);686}687688kadm5_ret_t689kadm5_c_init_with_skey_ctx(krb5_context context,690const char *client_name,691const char *keytab,692const char *service_name,693kadm5_config_params *realm_params,694unsigned long struct_version,695unsigned long api_version,696void **server_handle)697{698return kadm5_c_init_with_context(context,699client_name,700NULL,701NULL,702keytab,703NULL,704service_name,705realm_params,706struct_version,707api_version,708server_handle);709}710711712kadm5_ret_t713kadm5_c_init_with_skey(const char *client_name,714const char *keytab,715const char *service_name,716kadm5_config_params *realm_params,717unsigned long struct_version,718unsigned long api_version,719void **server_handle)720{721return init_context(client_name,722NULL,723NULL,724keytab,725NULL,726service_name,727realm_params,728struct_version,729api_version,730server_handle);731}732733kadm5_ret_t734kadm5_c_init_with_creds_ctx(krb5_context context,735const char *client_name,736krb5_ccache ccache,737const char *service_name,738kadm5_config_params *realm_params,739unsigned long struct_version,740unsigned long api_version,741void **server_handle)742{743return kadm5_c_init_with_context(context,744client_name,745NULL,746NULL,747NULL,748ccache,749service_name,750realm_params,751struct_version,752api_version,753server_handle);754}755756kadm5_ret_t757kadm5_c_init_with_creds(const char *client_name,758krb5_ccache ccache,759const char *service_name,760kadm5_config_params *realm_params,761unsigned long struct_version,762unsigned long api_version,763void **server_handle)764{765return init_context(client_name,766NULL,767NULL,768NULL,769ccache,770service_name,771realm_params,772struct_version,773api_version,774server_handle);775}776777#if 0778kadm5_ret_t779kadm5_init(char *client_name, char *pass,780char *service_name,781kadm5_config_params *realm_params,782unsigned long struct_version,783unsigned long api_version,784void **server_handle)785{786}787#endif788789790791