#define NEED_SOCKETS
#include <k5-int.h>
#include <kadm5/admin.h>
#include <client_internal.h>
#include <gssapi/gssapi.h>
#include <gssapi_krb5.h>
#include <gssapiP_krb5.h>
#include <krb5.h>
#include <stdio.h>
#include <errno.h>
extern krb5_error_code krb5int_mk_chpw_req(krb5_context context,
krb5_auth_context auth_context,
krb5_data *ap_req, char *passwd,
krb5_data *packet);
extern krb5_error_code krb5int_rd_chpw_rep(krb5_context context,
krb5_auth_context auth_context,
krb5_data *packet, int *result_code,
krb5_data *result_data);
krb5_chgpwd_prot
_kadm5_get_kpasswd_protocol(void *handle)
{
kadm5_server_handle_t srvrhdl = (kadm5_server_handle_t)handle;
return (srvrhdl->params.kpasswd_protocol);
}
static krb5_error_code
krb5_change_password_local(context, params, creds, newpw, srvr_rsp_code,
srvr_msg)
krb5_context context;
kadm5_config_params *params;
krb5_creds *creds;
char *newpw;
kadm5_ret_t *srvr_rsp_code;
krb5_data *srvr_msg;
{
krb5_auth_context auth_context;
krb5_data ap_req, chpw_req, chpw_rep;
krb5_address local_kaddr, remote_kaddr;
krb5_error_code code = 0;
int i, addrlen;
struct sockaddr *addr_p, local_addr, remote_addr, tmp_addr;
struct sockaddr_in *sin_p;
struct hostent *hp;
int naddr_p;
int cc, local_result_code, tmp_len;
SOCKET s1 = INVALID_SOCKET;
SOCKET s2 = INVALID_SOCKET;
auth_context = NULL;
addr_p = NULL;
memset(&chpw_req, 0, sizeof (krb5_data));
memset(&chpw_rep, 0, sizeof (krb5_data));
memset(&ap_req, 0, sizeof (krb5_data));
if ((code = krb5_auth_con_init(context, &auth_context)))
goto cleanup;
if (code = krb5_mk_req_extended(context, &auth_context,
AP_OPTS_USE_SUBKEY,
NULL, creds, &ap_req))
goto cleanup;
addr_p = (struct sockaddr *)malloc(sizeof (struct sockaddr));
if (!addr_p)
goto cleanup;
memset(addr_p, 0, sizeof (struct sockaddr));
if ((hp = gethostbyname(params->kpasswd_server)) == NULL) {
code = KRB5_REALM_CANT_RESOLVE;
goto cleanup;
}
sin_p = (struct sockaddr_in *)addr_p;
memset((char *)sin_p, 0, sizeof (struct sockaddr));
sin_p->sin_family = hp->h_addrtype;
sin_p->sin_port = htons(params->kpasswd_port);
memcpy((char *)&sin_p->sin_addr, (char *)hp->h_addr, hp->h_length);
naddr_p = 1;
if ((s1 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
code = errno;
goto cleanup;
}
if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
code = errno;
goto cleanup;
}
for (i = 0; i < naddr_p; i++)
{
fd_set fdset;
struct timeval timeout;
if (connect(s2, &addr_p[i], sizeof (addr_p[i])) ==
SOCKET_ERROR)
{
if ((errno == ECONNREFUSED) ||
(errno == EHOSTUNREACH))
continue;
code = errno;
goto cleanup;
}
addrlen = sizeof (local_addr);
if (getsockname(s2, &local_addr, &addrlen) < 0)
{
if ((errno == ECONNREFUSED) ||
(errno == EHOSTUNREACH))
continue;
code = errno;
goto cleanup;
}
if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0)
{
local_kaddr.addrtype = ADDRTYPE_INET;
local_kaddr.length = sizeof (((struct sockaddr_in *)
&local_addr)->sin_addr);
local_kaddr.contents = (krb5_octet *)
&(((struct sockaddr_in *)
&local_addr)->sin_addr);
}
else
{
krb5_address **addrs;
krb5_os_localaddr(context, &addrs);
local_kaddr.magic = addrs[0]->magic;
local_kaddr.addrtype = addrs[0]->addrtype;
local_kaddr.length = addrs[0]->length;
local_kaddr.contents = malloc(addrs[0]->length);
memcpy(local_kaddr.contents, addrs[0]->contents,
addrs[0]->length);
krb5_free_addresses(context, addrs);
}
addrlen = sizeof (remote_addr);
if (getpeername(s2, &remote_addr, &addrlen) < 0)
{
if ((errno == ECONNREFUSED) ||
(errno == EHOSTUNREACH))
continue;
code = errno;
goto cleanup;
}
remote_kaddr.addrtype = ADDRTYPE_INET;
remote_kaddr.length = sizeof (((struct sockaddr_in *)
&remote_addr)->sin_addr);
remote_kaddr.contents = (krb5_octet *)
&(((struct sockaddr_in *)&remote_addr)->sin_addr);
if (code = krb5_auth_con_setaddrs(context, auth_context,
&local_kaddr, NULL))
{
code = errno;
goto cleanup;
}
if (code = krb5int_mk_chpw_req(context, auth_context,
&ap_req, newpw, &chpw_req))
{
code = errno;
goto cleanup;
}
if ((cc = sendto(s1, chpw_req.data, chpw_req.length, 0,
(struct sockaddr *)&addr_p[i],
sizeof (addr_p[i]))) != chpw_req.length)
{
if ((cc < 0) && ((errno == ECONNREFUSED) ||
(errno == EHOSTUNREACH)))
continue;
code = (cc < 0) ? errno : ECONNABORTED;
goto cleanup;
}
chpw_rep.length = 1500;
chpw_rep.data = (char *)malloc(chpw_rep.length);
FD_ZERO(&fdset);
FD_SET(s1, &fdset);
timeout.tv_sec = 120;
timeout.tv_usec = 0;
switch (select(s1 + 1, &fdset, 0, 0, &timeout)) {
case -1:
code = errno;
goto cleanup;
case 0:
code = ETIMEDOUT;
goto cleanup;
default:
;
}
tmp_len = sizeof (tmp_addr);
if ((cc = recvfrom(s1, chpw_rep.data, chpw_rep.length,
0, &tmp_addr, &tmp_len)) < 0)
{
code = errno;
goto cleanup;
}
closesocket(s1);
s1 = INVALID_SOCKET;
closesocket(s2);
s2 = INVALID_SOCKET;
chpw_rep.length = cc;
if (code = krb5_auth_con_setaddrs(context, auth_context,
NULL, &remote_kaddr))
goto cleanup;
if (code = krb5int_rd_chpw_rep(context, auth_context, &chpw_rep,
&local_result_code, srvr_msg))
goto cleanup;
if (srvr_rsp_code)
*srvr_rsp_code = local_result_code;
code = 0;
goto cleanup;
}
code = errno;
cleanup:
if (auth_context != NULL)
krb5_auth_con_free(context, auth_context);
if (addr_p != NULL)
krb5_xfree(addr_p);
if (s1 != INVALID_SOCKET)
closesocket(s1);
if (s2 != INVALID_SOCKET)
closesocket(s2);
krb5_xfree(chpw_req.data);
krb5_xfree(chpw_rep.data);
krb5_xfree(ap_req.data);
return (code);
}
kadm5_ret_t
kadm5_chpass_principal_v2(void *server_handle,
krb5_principal princ,
char *newpw,
kadm5_ret_t *srvr_rsp_code,
krb5_data *srvr_msg)
{
kadm5_ret_t code;
kadm5_server_handle_t handle = (kadm5_server_handle_t)server_handle;
krb5_error_code result;
krb5_creds mcreds;
krb5_creds ncreds;
krb5_ccache ccache;
int cpwlen;
char *cpw_service = NULL;
memset(&mcreds, 0, sizeof (krb5_creds));
if ((code = krb5_cc_resolve(handle->context, handle->cache_name,
&ccache)))
return (code);
mcreds.client = princ;
cpwlen = strlen(KADM5_CHANGEPW_SERVICE) +
strlen(handle->params.realm) + 2;
cpw_service = malloc(cpwlen);
if (cpw_service == NULL) {
return (ENOMEM);
}
snprintf(cpw_service, cpwlen, "%s@%s",
KADM5_CHANGEPW_SERVICE, handle->params.realm);
if ((code = krb5_parse_name(handle->context, cpw_service,
&mcreds.server))) {
free(cpw_service);
return (code);
}
if ((code = krb5_cc_retrieve_cred(handle->context, ccache, 0, &mcreds,
&ncreds))) {
free(cpw_service);
return (code);
}
result = krb5_change_password_local(handle->context, &handle->params,
&ncreds, newpw,
srvr_rsp_code,
srvr_msg);
free(cpw_service);
return (result);
}