#include "k5-int.h"
#include "fake-addrinfo.h"
#include "os-proto.h"
#include "../krb/auth_con.h"
#include "../krb/int-proto.h"
#include <stdio.h>
#include <errno.h>
#ifndef GETSOCKNAME_ARG3_TYPE
#define GETSOCKNAME_ARG3_TYPE int
#endif
struct sendto_callback_context {
krb5_context context;
krb5_auth_context auth_context;
krb5_principal set_password_for;
const char *newpw;
krb5_data ap_req;
krb5_ui_4 remote_seq_num, local_seq_num;
};
static krb5_error_code
locate_kpasswd(krb5_context context, const krb5_data *realm,
struct serverlist *serverlist)
{
krb5_error_code code;
code = k5_locate_server(context, realm, serverlist, locate_service_kpasswd,
FALSE);
if (code == KRB5_REALM_CANT_RESOLVE || code == KRB5_REALM_UNKNOWN) {
code = k5_locate_server(context, realm, serverlist,
locate_service_kadmin, TRUE);
if (!code) {
size_t i;
for (i = 0; i < serverlist->nservers; i++) {
struct server_entry *s = &serverlist->servers[i];
if (s->transport == TCP)
s->transport = TCP_OR_UDP;
if (s->hostname != NULL)
s->port = DEFAULT_KPASSWD_PORT;
else if (s->family == AF_INET)
ss2sin(&s->addr)->sin_port = htons(DEFAULT_KPASSWD_PORT);
else if (s->family == AF_INET6)
ss2sin6(&s->addr)->sin6_port = htons(DEFAULT_KPASSWD_PORT);
}
}
}
return (code);
}
static void
kpasswd_sendto_msg_cleanup(void *data, krb5_data *message)
{
struct sendto_callback_context *ctx = data;
krb5_free_data_contents(ctx->context, message);
}
static int
kpasswd_sendto_msg_callback(SOCKET fd, void *data, krb5_data *message)
{
krb5_error_code code = 0;
struct sockaddr_storage local_addr;
krb5_address local_kaddr;
struct sendto_callback_context *ctx = data;
GETSOCKNAME_ARG3_TYPE addrlen;
krb5_data output;
krb5_address **addrs = NULL;
memset (message, 0, sizeof(krb5_data));
addrlen = sizeof(local_addr);
if (getsockname(fd, ss2sa(&local_addr), &addrlen) < 0) {
code = SOCKET_ERRNO;
goto cleanup;
}
if (local_addr.ss_family == AF_INET &&
ss2sin(&local_addr)->sin_addr.s_addr != 0) {
local_kaddr.addrtype = ADDRTYPE_INET;
local_kaddr.length = sizeof(ss2sin(&local_addr)->sin_addr);
local_kaddr.contents = (krb5_octet *) &ss2sin(&local_addr)->sin_addr;
} else if (local_addr.ss_family == AF_INET6 &&
memcmp(ss2sin6(&local_addr)->sin6_addr.s6_addr,
in6addr_any.s6_addr, sizeof(in6addr_any.s6_addr)) != 0) {
local_kaddr.addrtype = ADDRTYPE_INET6;
local_kaddr.length = sizeof(ss2sin6(&local_addr)->sin6_addr);
local_kaddr.contents = (krb5_octet *) &ss2sin6(&local_addr)->sin6_addr;
#ifndef _WIN32
} else if (local_addr.ss_family == AF_UNIX) {
local_kaddr = k5_addr_directional_init;
#endif
} else {
code = krb5_os_localaddr(ctx->context, &addrs);
if (code)
goto cleanup;
local_kaddr = *addrs[0];
}
if ((code = krb5_auth_con_setaddrs(ctx->context, ctx->auth_context,
&local_kaddr, NULL)))
goto cleanup;
ctx->auth_context->remote_seq_number = ctx->remote_seq_num;
ctx->auth_context->local_seq_number = ctx->local_seq_num;
if (ctx->set_password_for)
code = krb5int_mk_setpw_req(ctx->context,
ctx->auth_context,
&ctx->ap_req,
ctx->set_password_for,
ctx->newpw,
&output);
else
code = krb5int_mk_chpw_req(ctx->context,
ctx->auth_context,
&ctx->ap_req,
ctx->newpw,
&output);
if (code)
goto cleanup;
message->length = output.length;
message->data = output.data;
cleanup:
krb5_free_addresses(ctx->context, addrs);
return code;
}
static krb5_error_code
change_set_password(krb5_context context,
krb5_creds *creds,
const char *newpw,
krb5_principal set_password_for,
int *result_code,
krb5_data *result_code_string,
krb5_data *result_string)
{
krb5_data chpw_rep;
GETSOCKNAME_ARG3_TYPE addrlen;
krb5_error_code code = 0;
char *code_string;
int local_result_code;
struct sendto_callback_context callback_ctx;
struct sendto_callback_info callback_info;
struct sockaddr_storage remote_addr;
struct serverlist sl = SERVERLIST_INIT;
memset(&chpw_rep, 0, sizeof(krb5_data));
memset( &callback_ctx, 0, sizeof(struct sendto_callback_context));
callback_ctx.context = context;
callback_ctx.newpw = newpw;
callback_ctx.set_password_for = set_password_for;
if ((code = krb5_auth_con_init(callback_ctx.context,
&callback_ctx.auth_context)))
goto cleanup;
if ((code = krb5_mk_req_extended(callback_ctx.context,
&callback_ctx.auth_context,
AP_OPTS_USE_SUBKEY,
NULL,
creds,
&callback_ctx.ap_req)))
goto cleanup;
callback_ctx.remote_seq_num = callback_ctx.auth_context->remote_seq_number;
callback_ctx.local_seq_num = callback_ctx.auth_context->local_seq_number;
code = locate_kpasswd(callback_ctx.context, &creds->server->realm, &sl);
if (code)
goto cleanup;
addrlen = sizeof(remote_addr);
callback_info.data = &callback_ctx;
callback_info.pfn_callback = kpasswd_sendto_msg_callback;
callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup;
krb5_free_data_contents(callback_ctx.context, &chpw_rep);
code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
&sl, NO_UDP, &callback_info, &chpw_rep,
ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
if (code == KRB5_KDC_UNREACH) {
code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
&sl, ONLY_UDP, &callback_info, &chpw_rep,
ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
}
if (code)
goto cleanup;
code = krb5int_rd_chpw_rep(callback_ctx.context,
callback_ctx.auth_context,
&chpw_rep, &local_result_code,
result_string);
if (code)
goto cleanup;
if (result_code)
*result_code = local_result_code;
if (result_code_string) {
code = krb5_chpw_result_code_string(callback_ctx.context,
local_result_code,
&code_string);
if (code)
goto cleanup;
result_code_string->length = strlen(code_string);
result_code_string->data = malloc(result_code_string->length);
if (result_code_string->data == NULL) {
code = ENOMEM;
goto cleanup;
}
strncpy(result_code_string->data, code_string, result_code_string->length);
}
cleanup:
if (callback_ctx.auth_context != NULL)
krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context);
k5_free_serverlist(&sl);
krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req);
krb5_free_data_contents(callback_ctx.context, &chpw_rep);
return(code);
}
krb5_error_code KRB5_CALLCONV
krb5_change_password(krb5_context context,
krb5_creds *creds,
const char *newpw,
int *result_code,
krb5_data *result_code_string,
krb5_data *result_string)
{
return change_set_password(context, creds, newpw, NULL,
result_code, result_code_string, result_string );
}
krb5_error_code KRB5_CALLCONV
krb5_set_password(krb5_context context,
krb5_creds *creds,
const char *newpw,
krb5_principal change_password_for,
int *result_code,
krb5_data *result_code_string,
krb5_data *result_string
)
{
return change_set_password(context, creds, newpw, change_password_for,
result_code, result_code_string, result_string );
}
krb5_error_code KRB5_CALLCONV
krb5_set_password_using_ccache(krb5_context context,
krb5_ccache ccache,
const char *newpw,
krb5_principal change_password_for,
int *result_code,
krb5_data *result_code_string,
krb5_data *result_string
)
{
krb5_creds creds;
krb5_creds *credsp;
krb5_error_code code;
memset (&creds, 0, sizeof(creds));
code = krb5_cc_get_principal (context, ccache, &creds.client);
if (!code) {
code = krb5_build_principal(context, &creds.server,
change_password_for->realm.length,
change_password_for->realm.data,
"kadmin", "changepw", NULL);
if (!code) {
code = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
if (!code) {
code = krb5_set_password(context, credsp, newpw, change_password_for,
result_code, result_code_string,
result_string);
krb5_free_creds(context, credsp);
}
}
krb5_free_cred_contents(context, &creds);
}
return code;
}