/* Dispatch routine for set/change password */ void dispatch(void *handle, struct sockaddr *local_saddr, const krb5_fulladdr *remote_faddr, krb5_data *request, int is_tcp, verto_ctx *vctx, loop_respond_fn respond, void *arg) { krb5_error_code ret; krb5_keytab kt = NULL; kadm5_server_handle_t server_handle = (kadm5_server_handle_t)handle; krb5_fulladdr local_faddr; krb5_address **local_kaddrs = NULL, local_kaddr_buf; krb5_data *response = NULL; if (local_saddr == NULL) { ret = krb5_os_localaddr(server_handle->context, &local_kaddrs); if (ret != 0) goto egress; local_faddr.address = local_kaddrs[0]; local_faddr.port = 0; } else { local_faddr.address = &local_kaddr_buf; init_addr(&local_faddr, local_saddr); } ret = krb5_kt_resolve(server_handle->context, "KDB:", &kt); if (ret != 0) { krb5_klog_syslog(LOG_ERR, _("chpw: Couldn't open admin keytab %s"), krb5_get_error_message(server_handle->context, ret)); goto egress; } response = k5alloc(sizeof(krb5_data), &ret); if (response == NULL) goto egress; ret = process_chpw_request(server_handle->context, handle, server_handle->params.realm, kt, &local_faddr, remote_faddr, request, response); egress: if (ret) krb5_free_data(server_handle->context, response); krb5_free_addresses(server_handle->context, local_kaddrs); krb5_kt_close(server_handle->context, kt); (*respond)(arg, ret, ret == 0 ? response : NULL); }
/* * This routine is used to handle password-change requests received * on kpasswd-port 464 from MIT/M$ clients. */ void handle_chpw(krb5_context context, int s1, void *serverhandle, kadm5_config_params *params) { krb5_error_code ret; char req[MAXAPREQ]; int len; struct sockaddr_in from; int fromlen; krb5_keytab kt; krb5_data reqdata, repdata; int s2 = -1; reqdata.length = 0; reqdata.data = NULL; repdata.length = 0; repdata.data = NULL; fromlen = sizeof (from); if ((len = recvfrom(s1, req, sizeof (req), 0, (struct sockaddr *)&from, &fromlen)) < 0) { krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't receive " "request: %s"), error_message(errno)); return; } if ((ret = krb5_kt_resolve(context, params->admin_keytab, &kt))) { krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't open " "admin keytab %s"), error_message(ret)); return; } reqdata.length = len; reqdata.data = req; /* * This is really obscure. s1 is used for all communications. it * is left unconnected in case the server is multihomed and routes * are asymmetric. s2 is connected to resolve routes and get * addresses. this is the *only* way to get proper addresses for * multihomed hosts if routing is asymmetric. * * A related problem in the server, but not the client, is that * many os's have no way to disconnect a connected udp socket, so * the s2 socket needs to be closed and recreated for each * request. The s1 socket must not be closed, or else queued * requests will be lost. * * A "naive" client implementation (one socket, no connect, * hostname resolution to get the local ip addr) will work and * interoperate if the client is single-homed. */ if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { krb5_klog_syslog(LOG_ERR, gettext("chpw: Cannot create " "connecting socket: %s"), error_message(errno)); goto cleanup; } if (connect(s2, (struct sockaddr *)&from, sizeof (from)) < 0) { krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't connect " "to client: %s"), error_message(errno)); if (s2 > 0) (void) close(s2); goto cleanup; } if ((ret = process_chpw_request(context, serverhandle, params->realm, s2, kt, &from, &reqdata, &repdata))) { krb5_klog_syslog(LOG_ERR, gettext("chpw: Error processing " "request: %s"), error_message(ret)); } if (s2 > 0) (void) close(s2); if (repdata.length == 0 || repdata.data == NULL) { /* * Just return. This means something really bad happened */ goto cleanup; } len = sendto(s1, repdata.data, repdata.length, 0, (struct sockaddr *)&from, sizeof (from)); if (len < repdata.length) { krb5_xfree(repdata.data); krb5_klog_syslog(LOG_ERR, gettext("chpw: Error sending reply:" " %s"), error_message(errno)); goto cleanup; } if (repdata.data != NULL) krb5_xfree(repdata.data); cleanup: krb5_kt_close(context, kt); }