2N/A/*
2N/A * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * lib/krb5/kadm5/srv/chgpwd.c
2N/A *
2N/A * Copyright 1998 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A */
2N/A
2N/A/*
2N/A * chgpwd.c - Handles changepw requests issued from non-Solaris krb5 clients.
2N/A */
2N/A
2N/A/* Solaris Kerberos */
2N/A#include "k5-int.h"
2N/A#include <errno.h>
2N/A#include <string.h>
2N/A#include <sys/types.h>
2N/A#include <sys/socket.h>
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A
2N/A#include <libintl.h>
2N/A#include <locale.h>
2N/A#include <kadm5/admin.h>
2N/A#include <syslog.h>
2N/A#include <krb5/adm_proto.h>
2N/A
2N/A#define MAXAPREQ 1500
2N/A#define RFC3244_VERSION 0xff80
2N/A
2N/Astatic krb5_error_code
2N/Aprocess_chpw_request(krb5_context context, void *server_handle,
2N/A char *realm, int s, krb5_keytab keytab,
2N/A struct sockaddr_in *sin, krb5_data *req,
2N/A krb5_data *rep)
2N/A{
2N/A krb5_error_code ret;
2N/A char *ptr;
2N/A int plen, vno;
2N/A krb5_address local_kaddr, remote_kaddr;
2N/A int allocated_mem = 0;
2N/A krb5_data ap_req, ap_rep;
2N/A krb5_auth_context auth_context;
2N/A krb5_principal changepw;
2N/A krb5_principal client, target = NULL;
2N/A krb5_ticket *ticket;
2N/A krb5_data cipher, clear;
2N/A struct sockaddr local_addr, remote_addr;
2N/A socklen_t addrlen;
2N/A krb5_replay_data replay;
2N/A krb5_error krberror;
2N/A int numresult;
2N/A char strresult[1024];
2N/A char *clientstr = NULL, *targetstr = NULL;
2N/A size_t clen;
2N/A char *cdots;
2N/A
2N/A ret = 0;
2N/A rep->length = 0;
2N/A
2N/A auth_context = NULL;
2N/A changepw = NULL;
2N/A ap_rep.length = 0;
2N/A ap_rep.data = NULL;
2N/A ticket = NULL;
2N/A clear.length = 0;
2N/A clear.data = NULL;
2N/A cipher.length = 0;
2N/A cipher.data = NULL;
2N/A
2N/A if (req->length < 4) {
2N/A /*
2N/A * either this, or the server is printing bad messages,
2N/A * or the caller passed in garbage
2N/A */
2N/A ret = KRB5KRB_AP_ERR_MODIFIED;
2N/A numresult = KRB5_KPASSWD_MALFORMED;
2N/A (void) strlcpy(strresult, "Request was truncated",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A ptr = req->data;
2N/A
2N/A /*
2N/A * Verify length
2N/A */
2N/A plen = (*ptr++ & 0xff);
2N/A plen = (plen<<8) | (*ptr++ & 0xff);
2N/A
2N/A if (plen != req->length)
2N/A return (KRB5KRB_AP_ERR_MODIFIED);
2N/A
2N/A /*
2N/A * Verify version number
2N/A */
2N/A vno = (*ptr++ & 0xff);
2N/A vno = (vno<<8) | (*ptr++ & 0xff);
2N/A
2N/A if (vno != 1 && vno != RFC3244_VERSION) {
2N/A ret = KRB5KDC_ERR_BAD_PVNO;
2N/A numresult = KRB5_KPASSWD_MALFORMED;
2N/A (void) snprintf(strresult, sizeof (strresult),
2N/A "Request contained unknown protocol version number %d",
2N/A vno);
2N/A goto chpwfail;
2N/A }
2N/A
2N/A /*
2N/A * Read, check ap-req length
2N/A */
2N/A ap_req.length = (*ptr++ & 0xff);
2N/A ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);
2N/A
2N/A if (ptr + ap_req.length >= req->data + req->length) {
2N/A ret = KRB5KRB_AP_ERR_MODIFIED;
2N/A numresult = KRB5_KPASSWD_MALFORMED;
2N/A (void) strlcpy(strresult, "Request was truncated in AP-REQ",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A /*
2N/A * Verify ap_req
2N/A */
2N/A ap_req.data = ptr;
2N/A ptr += ap_req.length;
2N/A
2N/A if (ret = krb5_auth_con_init(context, &auth_context)) {
2N/A krb5_klog_syslog(LOG_ERR,
2N/A gettext("Change password request failed. "
2N/A "Failed initializing auth context: %s"),
2N/A error_message(ret));
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult, "Failed initializing auth context",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A if (ret = krb5_auth_con_setflags(context, auth_context,
2N/A KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
2N/A krb5_klog_syslog(LOG_ERR,
2N/A gettext("Change password request failed. "
2N/A "Failed setting auth "
2N/A "context flags: %s"),
2N/A error_message(ret));
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult, "Failed initializing auth context",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
2N/A "kadmin", "changepw", NULL)) {
2N/A krb5_klog_syslog(LOG_ERR,
2N/A gettext("Change password request failed "
2N/A "Failed to build kadmin/changepw "
2N/A "principal: %s"),
2N/A error_message(ret));
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult,
2N/A "Failed building kadmin/changepw principal",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
2N/A NULL, &ticket);
2N/A
2N/A if (ret) {
2N/A char kt_name[MAX_KEYTAB_NAME_LEN];
2N/A if (krb5_kt_get_name(context, keytab,
2N/A kt_name, sizeof (kt_name)))
2N/A strncpy(kt_name, "default keytab", sizeof (kt_name));
2N/A
2N/A switch (ret) {
2N/A case KRB5_KT_NOTFOUND:
2N/A krb5_klog_syslog(LOG_ERR,
2N/A gettext("Change password request failed because "
2N/A "keytab entry \"kadmin/changepw\" "
2N/A "is missing from \"%s\""),
2N/A kt_name);
2N/A break;
2N/A case ENOENT:
2N/A krb5_klog_syslog(LOG_ERR,
2N/A gettext("Change password request failed because "
2N/A "keytab file \"%s\" does not exist"),
2N/A kt_name);
2N/A break;
2N/A default:
2N/A krb5_klog_syslog(LOG_ERR,
2N/A gettext("Change password request failed. "
2N/A "Failed to parse Kerberos AP_REQ message: %s"),
2N/A error_message(ret));
2N/A }
2N/A
2N/A numresult = KRB5_KPASSWD_AUTHERROR;
2N/A (void) strlcpy(strresult, "Failed reading application request",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A /*
2N/A * Set up address info
2N/A */
2N/A addrlen = sizeof (local_addr);
2N/A
2N/A if (getsockname(s, &local_addr, &addrlen) < 0) {
2N/A ret = errno;
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult,
2N/A "Failed getting server internet address",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A /*
2N/A * Some brain-dead OS's don't return useful information from
2N/A * the getsockname call. Namely, windows and solaris.
2N/A */
2N/A if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) {
2N/A local_kaddr.addrtype = ADDRTYPE_INET;
2N/A local_kaddr.length = sizeof (((struct sockaddr_in *)
2N/A &local_addr)->sin_addr);
2N/A /* CSTYLED */
2N/A local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr);
2N/A } else {
2N/A krb5_address **addrs;
2N/A
2N/A krb5_os_localaddr(context, &addrs);
2N/A
2N/A local_kaddr.magic = addrs[0]->magic;
2N/A local_kaddr.addrtype = addrs[0]->addrtype;
2N/A local_kaddr.length = addrs[0]->length;
2N/A if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) {
2N/A ret = errno;
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult,
2N/A "Malloc failed for local_kaddr",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A (void) memcpy(local_kaddr.contents, addrs[0]->contents,
2N/A addrs[0]->length);
2N/A allocated_mem++;
2N/A
2N/A krb5_free_addresses(context, addrs);
2N/A }
2N/A
2N/A addrlen = sizeof (remote_addr);
2N/A
2N/A if (getpeername(s, &remote_addr, &addrlen) < 0) {
2N/A ret = errno;
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult,
2N/A "Failed getting client internet address",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A remote_kaddr.addrtype = ADDRTYPE_INET;
2N/A remote_kaddr.length = sizeof (((struct sockaddr_in *)
2N/A &remote_addr)->sin_addr);
2N/A /* CSTYLED */
2N/A remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr);
2N/A remote_kaddr.addrtype = ADDRTYPE_INET;
2N/A remote_kaddr.length = sizeof (sin->sin_addr);
2N/A remote_kaddr.contents = (krb5_octet *) &sin->sin_addr;
2N/A
2N/A /*
2N/A * mk_priv requires that the local address be set.
2N/A * getsockname is used for this. rd_priv requires that the
2N/A * remote address be set. recvfrom is used for this. If
2N/A * rd_priv is given a local address, and the message has the
2N/A * recipient addr in it, this will be checked. However, there
2N/A * is simply no way to know ahead of time what address the
2N/A * message will be delivered *to*. Therefore, it is important
2N/A * that either no recipient address is in the messages when
2N/A * mk_priv is called, or that no local address is passed to
2N/A * rd_priv. Both is a better idea, and I have done that. In
2N/A * summary, when mk_priv is called, *only* a local address is
2N/A * specified. when rd_priv is called, *only* a remote address
2N/A * is specified. Are we having fun yet?
2N/A */
2N/A if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL,
2N/A &remote_kaddr)) {
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult,
2N/A "Failed storing client internet address",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A /*
2N/A * Construct the ap-rep
2N/A */
2N/A if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) {
2N/A numresult = KRB5_KPASSWD_AUTHERROR;
2N/A (void) strlcpy(strresult,
2N/A "Failed replying to application request",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A /*
2N/A * Decrypt the new password
2N/A */
2N/A cipher.length = (req->data + req->length) - ptr;
2N/A cipher.data = ptr;
2N/A
2N/A if (ret = krb5_rd_priv(context, auth_context, &cipher,
2N/A &clear, &replay)) {
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult, "Failed decrypting request",
2N/A sizeof (strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A client = ticket->enc_part2->client;
2N/A
2N/A /* decode ChangePasswdData for setpw requests */
2N/A if (vno == RFC3244_VERSION) {
2N/A krb5_data *clear_data;
2N/A
2N/A ret = decode_krb5_setpw_req(&clear, &clear_data, &target);
2N/A if (ret != 0) {
2N/A numresult = KRB5_KPASSWD_MALFORMED;
2N/A strlcpy(strresult, "Failed decoding ChangePasswdData",
2N/A sizeof(strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A memset(clear.data, 0, clear.length);
2N/A free(clear.data);
2N/A
2N/A clear = *clear_data;
2N/A free(clear_data);
2N/A
2N/A if (target != NULL) {
2N/A ret = krb5_unparse_name(context, target, &targetstr);
2N/A if (ret != 0) {
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A strlcpy(strresult, "Failed unparsing target name for log",
2N/A sizeof(strresult));
2N/A goto chpwfail;
2N/A }
2N/A }
2N/A }
2N/A
2N/A ret = krb5_unparse_name(context, client, &clientstr);
2N/A if (ret) {
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strcpy(strresult, "Failed decrypting request");
2N/A goto chpwfail;
2N/A }
2N/A
2N/A /* for cpw, verify that this is an AS_REQ ticket */
2N/A if (vno == 1 &&
2N/A (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) {
2N/A numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
2N/A strlcpy(strresult, "Ticket must be derived from a password",
2N/A sizeof(strresult));
2N/A goto chpwfail;
2N/A }
2N/A
2N/A /*
2N/A * Change the password
2N/A */
2N/A if ((ptr = (char *)malloc(clear.length + 1)) == NULL) {
2N/A ret = errno;
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult, "Malloc failed for ptr",
2N/A sizeof (strresult));
2N/A krb5_free_unparsed_name(context, clientstr);
2N/A goto chpwfail;
2N/A }
2N/A (void) memcpy(ptr, clear.data, clear.length);
2N/A ptr[clear.length] = '\0';
2N/A
2N/A ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle,
2N/A client, ptr, NULL, strresult, sizeof (strresult));
2N/A
2N/A /*
2N/A * Zap the password
2N/A */
2N/A (void) memset(clear.data, 0, clear.length);
2N/A (void) memset(ptr, 0, clear.length);
2N/A if (clear.data != NULL) {
2N/A krb5_xfree(clear.data);
2N/A clear.data = NULL;
2N/A }
2N/A free(ptr);
2N/A clear.length = 0;
2N/A
2N/A clen = strlen(clientstr);
2N/A trunc_name(&clen, &cdots);
2N/A if (vno == RFC3244_VERSION) {
2N/A size_t tlen;
2N/A char *tdots;
2N/A const char *targetp;
2N/A
2N/A if (target == NULL) {
2N/A tlen = clen;
2N/A tdots = cdots;
2N/A targetp = targetstr;
2N/A } else {
2N/A tlen = strlen(targetstr);
2N/A trunc_name(&tlen, &tdots);
2N/A targetp = clientstr;
2N/A }
2N/A
2N/A krb5_klog_syslog(LOG_NOTICE, "setpw request from %s by %.*s%s "
2N/A "for %.*s%s: %s",
2N/A inet_ntoa(((struct sockaddr_in *)&remote_addr)->sin_addr),
2N/A (int) clen, clientstr, cdots, (int) tlen, targetp, tdots,
2N/A ret ? krb5_get_error_message (context, ret) : "success");
2N/A } else {
2N/A krb5_klog_syslog(LOG_NOTICE, "chpw request from %s for %.*s%s: %s",
2N/A inet_ntoa(((struct sockaddr_in *)&remote_addr)->sin_addr),
2N/A (int) clen, clientstr, cdots,
2N/A ret ? krb5_get_error_message (context, ret) : "success");
2N/A }
2N/A switch (ret) {
2N/A case KADM5_AUTH_CHANGEPW:
2N/A numresult = KRB5_KPASSWD_ACCESSDENIED;
2N/A break;
2N/A case KADM5_PASS_Q_TOOSHORT:
2N/A case KADM5_PASS_REUSE:
2N/A case KADM5_PASS_Q_CLASS:
2N/A case KADM5_PASS_Q_DICT:
2N/A case KADM5_PASS_TOOSOON:
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A break;
2N/A case 0:
2N/A numresult = KRB5_KPASSWD_SUCCESS;
2N/A strlcpy(strresult, "", sizeof(strresult));
2N/A break;
2N/A default:
2N/A numresult = KRB5_KPASSWD_SOFTERROR;
2N/A break;
2N/A }
2N/A
2N/Achpwfail:
2N/A
2N/A clear.length = 2 + strlen(strresult);
2N/A if (clear.data != NULL) {
2N/A krb5_xfree(clear.data);
2N/A clear.data = NULL;
2N/A }
2N/A if ((clear.data = (char *)malloc(clear.length)) == NULL) {
2N/A ret = errno;
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult, "Malloc failed for clear.data",
2N/A sizeof (strresult));
2N/A }
2N/A
2N/A ptr = clear.data;
2N/A
2N/A *ptr++ = (numresult>>8) & 0xff;
2N/A *ptr++ = numresult & 0xff;
2N/A
2N/A (void) memcpy(ptr, strresult, strlen(strresult));
2N/A
2N/A cipher.length = 0;
2N/A
2N/A if (ap_rep.length) {
2N/A if (ret = krb5_auth_con_setaddrs(context, auth_context,
2N/A &local_kaddr, NULL)) {
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult,
2N/A "Failed storing client and server internet addresses",
2N/A sizeof (strresult));
2N/A } else {
2N/A if (ret = krb5_mk_priv(context, auth_context, &clear,
2N/A &cipher, &replay)) {
2N/A numresult = KRB5_KPASSWD_HARDERROR;
2N/A (void) strlcpy(strresult,
2N/A "Failed encrypting reply",
2N/A sizeof (strresult));
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If no KRB-PRIV was constructed, then we need a KRB-ERROR.
2N/A * If this fails, just bail. There's nothing else we can do.
2N/A */
2N/A if (cipher.length == 0) {
2N/A /*
2N/A * Clear out ap_rep now, so that it won't be inserted
2N/A * in the reply
2N/A */
2N/A if (ap_rep.length) {
2N/A if (ap_rep.data != NULL)
2N/A krb5_xfree(ap_rep.data);
2N/A ap_rep.data = NULL;
2N/A ap_rep.length = 0;
2N/A }
2N/A
2N/A krberror.ctime = 0;
2N/A krberror.cusec = 0;
2N/A krberror.susec = 0;
2N/A if (ret = krb5_timeofday(context, &krberror.stime))
2N/A goto bailout;
2N/A
2N/A /*
2N/A * This is really icky. but it's what all the other callers
2N/A * to mk_error do.
2N/A */
2N/A krberror.error = ret;
2N/A krberror.error -= ERROR_TABLE_BASE_krb5;
2N/A if (krberror.error < 0 || krberror.error > 128)
2N/A krberror.error = KRB_ERR_GENERIC;
2N/A
2N/A krberror.client = NULL;
2N/A if (ret = krb5_build_principal(context, &krberror.server,
2N/A strlen(realm), realm,
2N/A "kadmin", "changepw", NULL)) {
2N/A goto bailout;
2N/A }
2N/A
2N/A krberror.text.length = 0;
2N/A krberror.e_data = clear;
2N/A
2N/A ret = krb5_mk_error(context, &krberror, &cipher);
2N/A
2N/A krb5_free_principal(context, krberror.server);
2N/A
2N/A if (ret)
2N/A goto bailout;
2N/A }
2N/A
2N/A /*
2N/A * Construct the reply
2N/A */
2N/A rep->length = 6 + ap_rep.length + cipher.length;
2N/A if ((rep->data = (char *)malloc(rep->length)) == NULL) {
2N/A ret = errno;
2N/A goto bailout;
2N/A }
2N/A ptr = rep->data;
2N/A
2N/A /*
2N/A * Length
2N/A */
2N/A *ptr++ = (rep->length>>8) & 0xff;
2N/A *ptr++ = rep->length & 0xff;
2N/A
2N/A /*
2N/A * Version == 0x0001 big-endian
2N/A */
2N/A *ptr++ = 0;
2N/A *ptr++ = 1;
2N/A
2N/A /*
2N/A * ap_rep length, big-endian
2N/A */
2N/A *ptr++ = (ap_rep.length>>8) & 0xff;
2N/A *ptr++ = ap_rep.length & 0xff;
2N/A
2N/A /*
2N/A * ap-rep data
2N/A */
2N/A if (ap_rep.length) {
2N/A (void) memcpy(ptr, ap_rep.data, ap_rep.length);
2N/A ptr += ap_rep.length;
2N/A }
2N/A
2N/A /*
2N/A * krb-priv or krb-error
2N/A */
2N/A (void) memcpy(ptr, cipher.data, cipher.length);
2N/A
2N/Abailout:
2N/A if (auth_context)
2N/A krb5_auth_con_free(context, auth_context);
2N/A if (changepw)
2N/A krb5_free_principal(context, changepw);
2N/A if (ap_rep.data != NULL)
2N/A krb5_xfree(ap_rep.data);
2N/A if (ticket)
2N/A krb5_free_ticket(context, ticket);
2N/A if (clear.data != NULL)
2N/A krb5_xfree(clear.data);
2N/A if (cipher.data != NULL)
2N/A krb5_xfree(cipher.data);
2N/A if (allocated_mem)
2N/A krb5_xfree(local_kaddr.contents);
2N/A if (target)
2N/A krb5_free_principal(context, target);
2N/A if (targetstr)
2N/A krb5_free_unparsed_name(context, targetstr);
2N/A if (clientstr)
2N/A krb5_free_unparsed_name(context, clientstr);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * This routine is used to handle password-change requests received
2N/A * on kpasswd-port 464 from MIT/M$ clients.
2N/A */
2N/Avoid
2N/Ahandle_chpw(krb5_context context, int s1,
2N/A void *serverhandle, kadm5_config_params *params)
2N/A{
2N/A krb5_error_code ret;
2N/A char req[MAXAPREQ];
2N/A int len;
2N/A struct sockaddr_in from;
2N/A socklen_t fromlen;
2N/A krb5_keytab kt;
2N/A krb5_data reqdata, repdata;
2N/A int s2 = -1;
2N/A
2N/A reqdata.length = 0;
2N/A reqdata.data = NULL;
2N/A repdata.length = 0;
2N/A repdata.data = NULL;
2N/A
2N/A fromlen = sizeof (from);
2N/A
2N/A if ((len = recvfrom(s1, req, sizeof (req), 0, (struct sockaddr *)&from,
2N/A &fromlen)) < 0) {
2N/A krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't receive "
2N/A "request: %s"), error_message(errno));
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * Solaris Kerberos:
2N/A * The only caller is kadmind, which is the master and therefore has the
2N/A * correct keys in the KDB, rather than obtaining them via the
2N/A * kadm5.keytab, by default.
2N/A */
2N/A if ((ret = krb5_kt_resolve(context, "KDB:", &kt))) {
2N/A krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't open "
2N/A "admin keytab %s"), error_message(ret));
2N/A return;
2N/A }
2N/A
2N/A reqdata.length = len;
2N/A reqdata.data = req;
2N/A
2N/A /*
2N/A * This is really obscure. s1 is used for all communications. it
2N/A * is left unconnected in case the server is multihomed and routes
2N/A * are asymmetric. s2 is connected to resolve routes and get
2N/A * addresses. this is the *only* way to get proper addresses for
2N/A * multihomed hosts if routing is asymmetric.
2N/A *
2N/A * A related problem in the server, but not the client, is that
2N/A * many os's have no way to disconnect a connected udp socket, so
2N/A * the s2 socket needs to be closed and recreated for each
2N/A * request. The s1 socket must not be closed, or else queued
2N/A * requests will be lost.
2N/A *
2N/A * A "naive" client implementation (one socket, no connect,
2N/A * hostname resolution to get the local ip addr) will work and
2N/A * interoperate if the client is single-homed.
2N/A */
2N/A
2N/A if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
2N/A krb5_klog_syslog(LOG_ERR, gettext("chpw: Cannot create "
2N/A "connecting socket: %s"), error_message(errno));
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (connect(s2, (struct sockaddr *)&from, sizeof (from)) < 0) {
2N/A krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't connect "
2N/A "to client: %s"), error_message(errno));
2N/A if (s2 > 0)
2N/A (void) close(s2);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((ret = process_chpw_request(context, serverhandle,
2N/A params->realm, s2, kt, &from,
2N/A &reqdata, &repdata))) {
2N/A krb5_klog_syslog(LOG_ERR, gettext("chpw: Error processing "
2N/A "request: %s"), error_message(ret));
2N/A }
2N/A
2N/A if (s2 > 0)
2N/A (void) close(s2);
2N/A
2N/A if (repdata.length == 0 || repdata.data == NULL) {
2N/A /*
2N/A * Just return. This means something really bad happened
2N/A */
2N/A goto cleanup;
2N/A }
2N/A
2N/A len = sendto(s1, repdata.data, repdata.length, 0,
2N/A (struct sockaddr *)&from, sizeof (from));
2N/A
2N/A if (len < repdata.length) {
2N/A krb5_xfree(repdata.data);
2N/A
2N/A krb5_klog_syslog(LOG_ERR, gettext("chpw: Error sending reply:"
2N/A " %s"), error_message(errno));
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (repdata.data != NULL)
2N/A krb5_xfree(repdata.data);
2N/Acleanup:
2N/A krb5_kt_close(context, kt);
2N/A}