xrdp/sesman/verify_user_kerberos.c

446 lines
10 KiB
C

/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2004-2012
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
* @file verify_user_kerberos.c
* @brief Authenticate user using kerberos
* @author Jay Sorg
*
*/
#include "arch.h"
#include "os_calls.h"
#include <krb5.h>
typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
struct k_opts
{
/* in seconds */
krb5_deltat starttime;
krb5_deltat lifetime;
krb5_deltat rlife;
int forwardable;
int proxiable;
int addresses;
int not_forwardable;
int not_proxiable;
int no_addresses;
int verbose;
char *principal_name;
char *service_name;
char *keytab_name;
char *k5_cache_name;
char *k4_cache_name;
action_type action;
};
struct k5_data
{
krb5_context ctx;
krb5_ccache cc;
krb5_principal me;
char *name;
};
struct user_info
{
char *name;
char *pass;
};
/******************************************************************************/
/* returns boolean */
static int DEFAULT_CC
k5_begin(struct k_opts *opts, struct k5_data *k5, struct user_info *u_info)
{
krb5_error_code code = 0;
code = krb5_init_context(&k5->ctx);
if (code != 0)
{
g_printf("krb5_init_context failed in k5_begin\n");
return 0;
}
if (opts->k5_cache_name)
{
code = krb5_cc_resolve(k5->ctx, opts->k5_cache_name, &k5->cc);
if (code != 0)
{
g_printf("krb5_cc_resolve failed in k5_begin\n");
return 0;
}
}
else
{
code = krb5_cc_default(k5->ctx, &k5->cc);
if (code != 0)
{
g_printf("krb5_cc_default failed in k5_begin\n");
return 0;
}
}
if (opts->principal_name)
{
/* Use specified name */
code = krb5_parse_name(k5->ctx, opts->principal_name, &k5->me);
if (code != 0)
{
g_printf("krb5_parse_name failed in k5_begin\n");
return 0;
}
}
else
{
/* No principal name specified */
if (opts->action == INIT_KT)
{
/* Use the default host/service name */
code = krb5_sname_to_principal(k5->ctx, NULL, NULL,
KRB5_NT_SRV_HST, &k5->me);
if (code != 0)
{
g_printf("krb5_sname_to_principal failed in k5_begin\n");
return 0;
}
}
else
{
/* Get default principal from cache if one exists */
code = krb5_cc_get_principal(k5->ctx, k5->cc, &k5->me);
if (code != 0)
{
code = krb5_parse_name(k5->ctx, u_info->name, &k5->me);
if (code != 0)
{
g_printf("krb5_parse_name failed in k5_begin\n");
return 0;
}
}
}
}
code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
if (code != 0)
{
g_printf("krb5_unparse_name failed in k5_begin\n");
return 0;
}
opts->principal_name = k5->name;
return 1;
}
/******************************************************************************/
static void DEFAULT_CC
k5_end(struct k5_data *k5)
{
if (k5->name)
{
krb5_free_unparsed_name(k5->ctx, k5->name);
}
if (k5->me)
{
krb5_free_principal(k5->ctx, k5->me);
}
if (k5->cc)
{
krb5_cc_close(k5->ctx, k5->cc);
}
if (k5->ctx)
{
krb5_free_context(k5->ctx);
}
g_memset(k5, 0, sizeof(struct k5_data));
}
/******************************************************************************/
static krb5_error_code KRB5_CALLCONV
kinit_prompter(krb5_context ctx, void *data, const char *name,
const char *banner, int num_prompts, krb5_prompt prompts[])
{
int i;
krb5_prompt_type *types;
krb5_error_code rc;
struct user_info *u_info;
u_info = (struct user_info *)data;
rc = 0;
types = krb5_get_prompt_types(ctx);
for (i = 0; i < num_prompts; i++)
{
if (types[i] == KRB5_PROMPT_TYPE_PASSWORD ||
types[i] == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN)
{
g_strncpy(prompts[i].reply->data, u_info->pass, 255);
}
}
return rc;
}
/******************************************************************************/
/* returns boolean */
static int
k5_kinit(struct k_opts *opts, struct k5_data *k5, struct user_info *u_info)
{
char *doing;
int notix = 1;
krb5_keytab keytab = 0;
krb5_creds my_creds;
krb5_error_code code = 0;
krb5_get_init_creds_opt options;
krb5_address **addresses;
krb5_get_init_creds_opt_init(&options);
g_memset(&my_creds, 0, sizeof(my_creds));
/*
From this point on, we can goto cleanup because my_creds is
initialized.
*/
if (opts->lifetime)
{
krb5_get_init_creds_opt_set_tkt_life(&options, opts->lifetime);
}
if (opts->rlife)
{
krb5_get_init_creds_opt_set_renew_life(&options, opts->rlife);
}
if (opts->forwardable)
{
krb5_get_init_creds_opt_set_forwardable(&options, 1);
}
if (opts->not_forwardable)
{
krb5_get_init_creds_opt_set_forwardable(&options, 0);
}
if (opts->proxiable)
{
krb5_get_init_creds_opt_set_proxiable(&options, 1);
}
if (opts->not_proxiable)
{
krb5_get_init_creds_opt_set_proxiable(&options, 0);
}
if (opts->addresses)
{
addresses = NULL;
code = krb5_os_localaddr(k5->ctx, &addresses);
if (code != 0)
{
g_printf("krb5_os_localaddr failed in k5_kinit\n");
goto cleanup;
}
krb5_get_init_creds_opt_set_address_list(&options, addresses);
}
if (opts->no_addresses)
{
krb5_get_init_creds_opt_set_address_list(&options, NULL);
}
if ((opts->action == INIT_KT) && opts->keytab_name)
{
code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab);
if (code != 0)
{
g_printf("krb5_kt_resolve failed in k5_kinit\n");
goto cleanup;
}
}
switch (opts->action)
{
case INIT_PW:
code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me,
0, kinit_prompter, u_info,
opts->starttime,
opts->service_name,
&options);
break;
case INIT_KT:
code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me,
keytab,
opts->starttime,
opts->service_name,
&options);
break;
case VALIDATE:
code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->cc,
opts->service_name);
break;
case RENEW:
code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->cc,
opts->service_name);
break;
}
if (code != 0)
{
doing = 0;
switch (opts->action)
{
case INIT_PW:
case INIT_KT:
doing = "getting initial credentials";
break;
case VALIDATE:
doing = "validating credentials";
break;
case RENEW:
doing = "renewing credentials";
break;
}
if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY)
{
g_printf("sesman: Password incorrect while %s in k5_kinit\n", doing);
}
else
{
g_printf("sesman: error while %s in k5_kinit\n", doing);
}
goto cleanup;
}
if (!opts->lifetime)
{
/* We need to figure out what lifetime to use for Kerberos 4. */
opts->lifetime = my_creds.times.endtime - my_creds.times.authtime;
}
code = krb5_cc_initialize(k5->ctx, k5->cc, k5->me);
if (code != 0)
{
g_printf("krb5_cc_initialize failed in k5_kinit\n");
goto cleanup;
}
code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds);
if (code != 0)
{
g_printf("krb5_cc_store_cred failed in k5_kinit\n");
goto cleanup;
}
notix = 0;
cleanup:
if (my_creds.client == k5->me)
{
my_creds.client = 0;
}
krb5_free_cred_contents(k5->ctx, &my_creds);
if (keytab)
{
krb5_kt_close(k5->ctx, keytab);
}
return notix ? 0 : 1;
}
/******************************************************************************/
/* returns boolean */
int DEFAULT_CC
auth_userpass(char *user, char *pass)
{
struct k_opts opts;
struct k5_data k5;
struct user_info u_info;
int got_k5;
int authed_k5;
g_memset(&opts, 0, sizeof(opts));
opts.action = INIT_PW;
g_memset(&k5, 0, sizeof(k5));
g_memset(&u_info, 0, sizeof(u_info));
u_info.name = user;
u_info.pass = pass;
authed_k5 = 0;
got_k5 = k5_begin(&opts, &k5, &u_info);
if (got_k5)
{
authed_k5 = k5_kinit(&opts, &k5, &u_info);
k5_end(&k5);
}
return authed_k5;
}
/******************************************************************************/
/* returns error */
int DEFAULT_CC
auth_start_session(void)
{
return 0;
}
/******************************************************************************/
int DEFAULT_CC
auth_end(void)
{
return 0;
}
/******************************************************************************/
int DEFAULT_CC
auth_set_env(void)
{
return 0;
}