1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert #include "k5-int.h"
3*7f2fe78bSCy Schubert #include <kadm5/admin.h>
4*7f2fe78bSCy Schubert #include <syslog.h>
5*7f2fe78bSCy Schubert #include <adm_proto.h> /* krb5_klog_syslog */
6*7f2fe78bSCy Schubert #include <stdio.h>
7*7f2fe78bSCy Schubert #include <errno.h>
8*7f2fe78bSCy Schubert
9*7f2fe78bSCy Schubert #include "kadm5/server_internal.h" /* XXX for kadm5_server_handle_t */
10*7f2fe78bSCy Schubert
11*7f2fe78bSCy Schubert #include "misc.h"
12*7f2fe78bSCy Schubert
13*7f2fe78bSCy Schubert #ifndef GETSOCKNAME_ARG3_TYPE
14*7f2fe78bSCy Schubert #define GETSOCKNAME_ARG3_TYPE int
15*7f2fe78bSCy Schubert #endif
16*7f2fe78bSCy Schubert
17*7f2fe78bSCy Schubert #define RFC3244_VERSION 0xff80
18*7f2fe78bSCy Schubert
19*7f2fe78bSCy Schubert static krb5_error_code
process_chpw_request(krb5_context context,void * server_handle,char * realm,krb5_keytab keytab,const krb5_fulladdr * local_addr,const krb5_fulladdr * remote_addr,krb5_data * req,krb5_data * rep)20*7f2fe78bSCy Schubert process_chpw_request(krb5_context context, void *server_handle, char *realm,
21*7f2fe78bSCy Schubert krb5_keytab keytab, const krb5_fulladdr *local_addr,
22*7f2fe78bSCy Schubert const krb5_fulladdr *remote_addr, krb5_data *req,
23*7f2fe78bSCy Schubert krb5_data *rep)
24*7f2fe78bSCy Schubert {
25*7f2fe78bSCy Schubert krb5_error_code ret;
26*7f2fe78bSCy Schubert char *ptr;
27*7f2fe78bSCy Schubert unsigned int plen, vno;
28*7f2fe78bSCy Schubert krb5_data ap_req, ap_rep = empty_data();
29*7f2fe78bSCy Schubert krb5_data cipher = empty_data(), clear = empty_data();
30*7f2fe78bSCy Schubert krb5_auth_context auth_context = NULL;
31*7f2fe78bSCy Schubert krb5_principal changepw = NULL;
32*7f2fe78bSCy Schubert krb5_principal client, target = NULL;
33*7f2fe78bSCy Schubert krb5_ticket *ticket = NULL;
34*7f2fe78bSCy Schubert krb5_replay_data replay;
35*7f2fe78bSCy Schubert krb5_error krberror;
36*7f2fe78bSCy Schubert int numresult;
37*7f2fe78bSCy Schubert char strresult[1024];
38*7f2fe78bSCy Schubert char *clientstr = NULL, *targetstr = NULL;
39*7f2fe78bSCy Schubert const char *errmsg = NULL;
40*7f2fe78bSCy Schubert size_t clen;
41*7f2fe78bSCy Schubert char *cdots;
42*7f2fe78bSCy Schubert struct sockaddr_storage ss;
43*7f2fe78bSCy Schubert socklen_t salen;
44*7f2fe78bSCy Schubert char addrbuf[100];
45*7f2fe78bSCy Schubert krb5_address *addr = remote_addr->address;
46*7f2fe78bSCy Schubert
47*7f2fe78bSCy Schubert *rep = empty_data();
48*7f2fe78bSCy Schubert
49*7f2fe78bSCy Schubert if (req->length < 4) {
50*7f2fe78bSCy Schubert /* either this, or the server is printing bad messages,
51*7f2fe78bSCy Schubert or the caller passed in garbage */
52*7f2fe78bSCy Schubert ret = KRB5KRB_AP_ERR_MODIFIED;
53*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_MALFORMED;
54*7f2fe78bSCy Schubert strlcpy(strresult, "Request was truncated", sizeof(strresult));
55*7f2fe78bSCy Schubert goto bailout;
56*7f2fe78bSCy Schubert }
57*7f2fe78bSCy Schubert
58*7f2fe78bSCy Schubert ptr = req->data;
59*7f2fe78bSCy Schubert
60*7f2fe78bSCy Schubert /* verify length */
61*7f2fe78bSCy Schubert
62*7f2fe78bSCy Schubert plen = (*ptr++ & 0xff);
63*7f2fe78bSCy Schubert plen = (plen<<8) | (*ptr++ & 0xff);
64*7f2fe78bSCy Schubert
65*7f2fe78bSCy Schubert if (plen != req->length) {
66*7f2fe78bSCy Schubert ret = KRB5KRB_AP_ERR_MODIFIED;
67*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_MALFORMED;
68*7f2fe78bSCy Schubert strlcpy(strresult, "Request length was inconsistent",
69*7f2fe78bSCy Schubert sizeof(strresult));
70*7f2fe78bSCy Schubert goto bailout;
71*7f2fe78bSCy Schubert }
72*7f2fe78bSCy Schubert
73*7f2fe78bSCy Schubert /* verify version number */
74*7f2fe78bSCy Schubert
75*7f2fe78bSCy Schubert vno = (*ptr++ & 0xff) ;
76*7f2fe78bSCy Schubert vno = (vno<<8) | (*ptr++ & 0xff);
77*7f2fe78bSCy Schubert
78*7f2fe78bSCy Schubert if (vno != 1 && vno != RFC3244_VERSION) {
79*7f2fe78bSCy Schubert ret = KRB5KDC_ERR_BAD_PVNO;
80*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_BAD_VERSION;
81*7f2fe78bSCy Schubert snprintf(strresult, sizeof(strresult),
82*7f2fe78bSCy Schubert "Request contained unknown protocol version number %d", vno);
83*7f2fe78bSCy Schubert goto bailout;
84*7f2fe78bSCy Schubert }
85*7f2fe78bSCy Schubert
86*7f2fe78bSCy Schubert /* read, check ap-req length */
87*7f2fe78bSCy Schubert
88*7f2fe78bSCy Schubert ap_req.length = (*ptr++ & 0xff);
89*7f2fe78bSCy Schubert ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);
90*7f2fe78bSCy Schubert
91*7f2fe78bSCy Schubert if (ptr + ap_req.length >= req->data + req->length) {
92*7f2fe78bSCy Schubert ret = KRB5KRB_AP_ERR_MODIFIED;
93*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_MALFORMED;
94*7f2fe78bSCy Schubert strlcpy(strresult, "Request was truncated in AP-REQ",
95*7f2fe78bSCy Schubert sizeof(strresult));
96*7f2fe78bSCy Schubert goto bailout;
97*7f2fe78bSCy Schubert }
98*7f2fe78bSCy Schubert
99*7f2fe78bSCy Schubert /* verify ap_req */
100*7f2fe78bSCy Schubert
101*7f2fe78bSCy Schubert ap_req.data = ptr;
102*7f2fe78bSCy Schubert ptr += ap_req.length;
103*7f2fe78bSCy Schubert
104*7f2fe78bSCy Schubert ret = krb5_auth_con_init(context, &auth_context);
105*7f2fe78bSCy Schubert if (ret) {
106*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_HARDERROR;
107*7f2fe78bSCy Schubert strlcpy(strresult, "Failed initializing auth context",
108*7f2fe78bSCy Schubert sizeof(strresult));
109*7f2fe78bSCy Schubert goto chpwfail;
110*7f2fe78bSCy Schubert }
111*7f2fe78bSCy Schubert
112*7f2fe78bSCy Schubert ret = krb5_auth_con_setflags(context, auth_context,
113*7f2fe78bSCy Schubert KRB5_AUTH_CONTEXT_DO_SEQUENCE);
114*7f2fe78bSCy Schubert if (ret) {
115*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_HARDERROR;
116*7f2fe78bSCy Schubert strlcpy(strresult, "Failed initializing auth context",
117*7f2fe78bSCy Schubert sizeof(strresult));
118*7f2fe78bSCy Schubert goto chpwfail;
119*7f2fe78bSCy Schubert }
120*7f2fe78bSCy Schubert
121*7f2fe78bSCy Schubert ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
122*7f2fe78bSCy Schubert "kadmin", "changepw", NULL);
123*7f2fe78bSCy Schubert if (ret) {
124*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_HARDERROR;
125*7f2fe78bSCy Schubert strlcpy(strresult, "Failed building kadmin/changepw principal",
126*7f2fe78bSCy Schubert sizeof(strresult));
127*7f2fe78bSCy Schubert goto chpwfail;
128*7f2fe78bSCy Schubert }
129*7f2fe78bSCy Schubert
130*7f2fe78bSCy Schubert ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
131*7f2fe78bSCy Schubert NULL, &ticket);
132*7f2fe78bSCy Schubert
133*7f2fe78bSCy Schubert if (ret) {
134*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_AUTHERROR;
135*7f2fe78bSCy Schubert strlcpy(strresult, "Failed reading application request",
136*7f2fe78bSCy Schubert sizeof(strresult));
137*7f2fe78bSCy Schubert goto chpwfail;
138*7f2fe78bSCy Schubert }
139*7f2fe78bSCy Schubert
140*7f2fe78bSCy Schubert /* construct the ap-rep */
141*7f2fe78bSCy Schubert
142*7f2fe78bSCy Schubert ret = krb5_mk_rep(context, auth_context, &ap_rep);
143*7f2fe78bSCy Schubert if (ret) {
144*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_AUTHERROR;
145*7f2fe78bSCy Schubert strlcpy(strresult, "Failed replying to application request",
146*7f2fe78bSCy Schubert sizeof(strresult));
147*7f2fe78bSCy Schubert goto chpwfail;
148*7f2fe78bSCy Schubert }
149*7f2fe78bSCy Schubert
150*7f2fe78bSCy Schubert /* decrypt the ChangePasswdData */
151*7f2fe78bSCy Schubert
152*7f2fe78bSCy Schubert cipher.length = (req->data + req->length) - ptr;
153*7f2fe78bSCy Schubert cipher.data = ptr;
154*7f2fe78bSCy Schubert
155*7f2fe78bSCy Schubert /*
156*7f2fe78bSCy Schubert * Don't set a remote address in auth_context before calling krb5_rd_priv,
157*7f2fe78bSCy Schubert * so that we can work against clients behind a NAT. Reflection attacks
158*7f2fe78bSCy Schubert * aren't a concern since we use sequence numbers and since our requests
159*7f2fe78bSCy Schubert * don't look anything like our responses. Also don't set a local address,
160*7f2fe78bSCy Schubert * since we don't know what interface the request was received on.
161*7f2fe78bSCy Schubert */
162*7f2fe78bSCy Schubert
163*7f2fe78bSCy Schubert ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay);
164*7f2fe78bSCy Schubert if (ret) {
165*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_HARDERROR;
166*7f2fe78bSCy Schubert strlcpy(strresult, "Failed decrypting request", sizeof(strresult));
167*7f2fe78bSCy Schubert goto chpwfail;
168*7f2fe78bSCy Schubert }
169*7f2fe78bSCy Schubert
170*7f2fe78bSCy Schubert client = ticket->enc_part2->client;
171*7f2fe78bSCy Schubert
172*7f2fe78bSCy Schubert /* decode ChangePasswdData for setpw requests */
173*7f2fe78bSCy Schubert if (vno == RFC3244_VERSION) {
174*7f2fe78bSCy Schubert krb5_data *clear_data;
175*7f2fe78bSCy Schubert
176*7f2fe78bSCy Schubert ret = decode_krb5_setpw_req(&clear, &clear_data, &target);
177*7f2fe78bSCy Schubert if (ret != 0) {
178*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_MALFORMED;
179*7f2fe78bSCy Schubert strlcpy(strresult, "Failed decoding ChangePasswdData",
180*7f2fe78bSCy Schubert sizeof(strresult));
181*7f2fe78bSCy Schubert goto chpwfail;
182*7f2fe78bSCy Schubert }
183*7f2fe78bSCy Schubert
184*7f2fe78bSCy Schubert zapfree(clear.data, clear.length);
185*7f2fe78bSCy Schubert
186*7f2fe78bSCy Schubert clear = *clear_data;
187*7f2fe78bSCy Schubert free(clear_data);
188*7f2fe78bSCy Schubert
189*7f2fe78bSCy Schubert if (target != NULL) {
190*7f2fe78bSCy Schubert ret = krb5_unparse_name(context, target, &targetstr);
191*7f2fe78bSCy Schubert if (ret != 0) {
192*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_HARDERROR;
193*7f2fe78bSCy Schubert strlcpy(strresult, "Failed unparsing target name for log",
194*7f2fe78bSCy Schubert sizeof(strresult));
195*7f2fe78bSCy Schubert goto chpwfail;
196*7f2fe78bSCy Schubert }
197*7f2fe78bSCy Schubert }
198*7f2fe78bSCy Schubert }
199*7f2fe78bSCy Schubert
200*7f2fe78bSCy Schubert ret = krb5_unparse_name(context, client, &clientstr);
201*7f2fe78bSCy Schubert if (ret) {
202*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_HARDERROR;
203*7f2fe78bSCy Schubert strlcpy(strresult, "Failed unparsing client name for log",
204*7f2fe78bSCy Schubert sizeof(strresult));
205*7f2fe78bSCy Schubert goto chpwfail;
206*7f2fe78bSCy Schubert }
207*7f2fe78bSCy Schubert
208*7f2fe78bSCy Schubert /* change the password */
209*7f2fe78bSCy Schubert
210*7f2fe78bSCy Schubert ptr = k5memdup0(clear.data, clear.length, &ret);
211*7f2fe78bSCy Schubert ret = schpw_util_wrapper(server_handle, client, target,
212*7f2fe78bSCy Schubert (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0,
213*7f2fe78bSCy Schubert ptr, NULL, strresult, sizeof(strresult));
214*7f2fe78bSCy Schubert if (ret)
215*7f2fe78bSCy Schubert errmsg = krb5_get_error_message(context, ret);
216*7f2fe78bSCy Schubert
217*7f2fe78bSCy Schubert /* zap the password */
218*7f2fe78bSCy Schubert zapfree(clear.data, clear.length);
219*7f2fe78bSCy Schubert zapfree(ptr, clear.length);
220*7f2fe78bSCy Schubert clear = empty_data();
221*7f2fe78bSCy Schubert
222*7f2fe78bSCy Schubert clen = strlen(clientstr);
223*7f2fe78bSCy Schubert trunc_name(&clen, &cdots);
224*7f2fe78bSCy Schubert
225*7f2fe78bSCy Schubert switch (addr->addrtype) {
226*7f2fe78bSCy Schubert case ADDRTYPE_INET: {
227*7f2fe78bSCy Schubert struct sockaddr_in *sin = ss2sin(&ss);
228*7f2fe78bSCy Schubert
229*7f2fe78bSCy Schubert sin->sin_family = AF_INET;
230*7f2fe78bSCy Schubert memcpy(&sin->sin_addr, addr->contents, addr->length);
231*7f2fe78bSCy Schubert sin->sin_port = htons(remote_addr->port);
232*7f2fe78bSCy Schubert salen = sizeof(*sin);
233*7f2fe78bSCy Schubert break;
234*7f2fe78bSCy Schubert }
235*7f2fe78bSCy Schubert case ADDRTYPE_INET6: {
236*7f2fe78bSCy Schubert struct sockaddr_in6 *sin6 = ss2sin6(&ss);
237*7f2fe78bSCy Schubert
238*7f2fe78bSCy Schubert sin6->sin6_family = AF_INET6;
239*7f2fe78bSCy Schubert memcpy(&sin6->sin6_addr, addr->contents, addr->length);
240*7f2fe78bSCy Schubert sin6->sin6_port = htons(remote_addr->port);
241*7f2fe78bSCy Schubert salen = sizeof(*sin6);
242*7f2fe78bSCy Schubert break;
243*7f2fe78bSCy Schubert }
244*7f2fe78bSCy Schubert default: {
245*7f2fe78bSCy Schubert struct sockaddr *sa = ss2sa(&ss);
246*7f2fe78bSCy Schubert
247*7f2fe78bSCy Schubert sa->sa_family = AF_UNSPEC;
248*7f2fe78bSCy Schubert salen = sizeof(*sa);
249*7f2fe78bSCy Schubert break;
250*7f2fe78bSCy Schubert }
251*7f2fe78bSCy Schubert }
252*7f2fe78bSCy Schubert
253*7f2fe78bSCy Schubert if (getnameinfo(ss2sa(&ss), salen,
254*7f2fe78bSCy Schubert addrbuf, sizeof(addrbuf), NULL, 0,
255*7f2fe78bSCy Schubert NI_NUMERICHOST | NI_NUMERICSERV) != 0)
256*7f2fe78bSCy Schubert strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf));
257*7f2fe78bSCy Schubert
258*7f2fe78bSCy Schubert if (vno == RFC3244_VERSION) {
259*7f2fe78bSCy Schubert size_t tlen;
260*7f2fe78bSCy Schubert char *tdots;
261*7f2fe78bSCy Schubert const char *targetp;
262*7f2fe78bSCy Schubert
263*7f2fe78bSCy Schubert if (target == NULL) {
264*7f2fe78bSCy Schubert tlen = clen;
265*7f2fe78bSCy Schubert tdots = cdots;
266*7f2fe78bSCy Schubert targetp = targetstr;
267*7f2fe78bSCy Schubert } else {
268*7f2fe78bSCy Schubert tlen = strlen(targetstr);
269*7f2fe78bSCy Schubert trunc_name(&tlen, &tdots);
270*7f2fe78bSCy Schubert targetp = clientstr;
271*7f2fe78bSCy Schubert }
272*7f2fe78bSCy Schubert
273*7f2fe78bSCy Schubert krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for "
274*7f2fe78bSCy Schubert "%.*s%s: %s"), addrbuf, (int) clen,
275*7f2fe78bSCy Schubert clientstr, cdots, (int) tlen, targetp, tdots,
276*7f2fe78bSCy Schubert errmsg ? errmsg : "success");
277*7f2fe78bSCy Schubert } else {
278*7f2fe78bSCy Schubert krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"),
279*7f2fe78bSCy Schubert addrbuf, (int) clen, clientstr, cdots,
280*7f2fe78bSCy Schubert errmsg ? errmsg : "success");
281*7f2fe78bSCy Schubert }
282*7f2fe78bSCy Schubert switch (ret) {
283*7f2fe78bSCy Schubert case KADM5_AUTH_CHANGEPW:
284*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_ACCESSDENIED;
285*7f2fe78bSCy Schubert break;
286*7f2fe78bSCy Schubert case KADM5_AUTH_INITIAL:
287*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
288*7f2fe78bSCy Schubert break;
289*7f2fe78bSCy Schubert case KADM5_PASS_Q_TOOSHORT:
290*7f2fe78bSCy Schubert case KADM5_PASS_REUSE:
291*7f2fe78bSCy Schubert case KADM5_PASS_Q_CLASS:
292*7f2fe78bSCy Schubert case KADM5_PASS_Q_DICT:
293*7f2fe78bSCy Schubert case KADM5_PASS_Q_GENERIC:
294*7f2fe78bSCy Schubert case KADM5_PASS_TOOSOON:
295*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_SOFTERROR;
296*7f2fe78bSCy Schubert break;
297*7f2fe78bSCy Schubert case 0:
298*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_SUCCESS;
299*7f2fe78bSCy Schubert strlcpy(strresult, "", sizeof(strresult));
300*7f2fe78bSCy Schubert break;
301*7f2fe78bSCy Schubert default:
302*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_HARDERROR;
303*7f2fe78bSCy Schubert break;
304*7f2fe78bSCy Schubert }
305*7f2fe78bSCy Schubert
306*7f2fe78bSCy Schubert chpwfail:
307*7f2fe78bSCy Schubert
308*7f2fe78bSCy Schubert ret = alloc_data(&clear, 2 + strlen(strresult));
309*7f2fe78bSCy Schubert if (ret)
310*7f2fe78bSCy Schubert goto bailout;
311*7f2fe78bSCy Schubert
312*7f2fe78bSCy Schubert ptr = clear.data;
313*7f2fe78bSCy Schubert
314*7f2fe78bSCy Schubert *ptr++ = (numresult>>8) & 0xff;
315*7f2fe78bSCy Schubert *ptr++ = numresult & 0xff;
316*7f2fe78bSCy Schubert
317*7f2fe78bSCy Schubert memcpy(ptr, strresult, strlen(strresult));
318*7f2fe78bSCy Schubert
319*7f2fe78bSCy Schubert cipher = empty_data();
320*7f2fe78bSCy Schubert
321*7f2fe78bSCy Schubert if (ap_rep.length) {
322*7f2fe78bSCy Schubert ret = krb5_auth_con_setaddrs(context, auth_context,
323*7f2fe78bSCy Schubert local_addr->address, NULL);
324*7f2fe78bSCy Schubert if (ret) {
325*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_HARDERROR;
326*7f2fe78bSCy Schubert strlcpy(strresult,
327*7f2fe78bSCy Schubert "Failed storing client and server internet addresses",
328*7f2fe78bSCy Schubert sizeof(strresult));
329*7f2fe78bSCy Schubert } else {
330*7f2fe78bSCy Schubert ret = krb5_mk_priv(context, auth_context, &clear, &cipher,
331*7f2fe78bSCy Schubert &replay);
332*7f2fe78bSCy Schubert if (ret) {
333*7f2fe78bSCy Schubert numresult = KRB5_KPASSWD_HARDERROR;
334*7f2fe78bSCy Schubert strlcpy(strresult, "Failed encrypting reply",
335*7f2fe78bSCy Schubert sizeof(strresult));
336*7f2fe78bSCy Schubert }
337*7f2fe78bSCy Schubert }
338*7f2fe78bSCy Schubert }
339*7f2fe78bSCy Schubert
340*7f2fe78bSCy Schubert /* if no KRB-PRIV was constructed, then we need a KRB-ERROR.
341*7f2fe78bSCy Schubert if this fails, just bail. there's nothing else we can do. */
342*7f2fe78bSCy Schubert
343*7f2fe78bSCy Schubert if (cipher.length == 0) {
344*7f2fe78bSCy Schubert /* clear out ap_rep now, so that it won't be inserted in the
345*7f2fe78bSCy Schubert reply */
346*7f2fe78bSCy Schubert
347*7f2fe78bSCy Schubert if (ap_rep.length) {
348*7f2fe78bSCy Schubert free(ap_rep.data);
349*7f2fe78bSCy Schubert ap_rep = empty_data();
350*7f2fe78bSCy Schubert }
351*7f2fe78bSCy Schubert
352*7f2fe78bSCy Schubert krberror.ctime = 0;
353*7f2fe78bSCy Schubert krberror.cusec = 0;
354*7f2fe78bSCy Schubert krberror.susec = 0;
355*7f2fe78bSCy Schubert ret = krb5_timeofday(context, &krberror.stime);
356*7f2fe78bSCy Schubert if (ret)
357*7f2fe78bSCy Schubert goto bailout;
358*7f2fe78bSCy Schubert
359*7f2fe78bSCy Schubert /* this is really icky. but it's what all the other callers
360*7f2fe78bSCy Schubert to mk_error do. */
361*7f2fe78bSCy Schubert krberror.error = ret;
362*7f2fe78bSCy Schubert krberror.error -= ERROR_TABLE_BASE_krb5;
363*7f2fe78bSCy Schubert if (krberror.error > KRB_ERR_MAX)
364*7f2fe78bSCy Schubert krberror.error = KRB_ERR_GENERIC;
365*7f2fe78bSCy Schubert
366*7f2fe78bSCy Schubert krberror.client = NULL;
367*7f2fe78bSCy Schubert
368*7f2fe78bSCy Schubert ret = krb5_build_principal(context, &krberror.server,
369*7f2fe78bSCy Schubert strlen(realm), realm,
370*7f2fe78bSCy Schubert "kadmin", "changepw", NULL);
371*7f2fe78bSCy Schubert if (ret)
372*7f2fe78bSCy Schubert goto bailout;
373*7f2fe78bSCy Schubert krberror.text.length = 0;
374*7f2fe78bSCy Schubert krberror.e_data = clear;
375*7f2fe78bSCy Schubert
376*7f2fe78bSCy Schubert ret = krb5_mk_error(context, &krberror, &cipher);
377*7f2fe78bSCy Schubert
378*7f2fe78bSCy Schubert krb5_free_principal(context, krberror.server);
379*7f2fe78bSCy Schubert
380*7f2fe78bSCy Schubert if (ret)
381*7f2fe78bSCy Schubert goto bailout;
382*7f2fe78bSCy Schubert }
383*7f2fe78bSCy Schubert
384*7f2fe78bSCy Schubert /* construct the reply */
385*7f2fe78bSCy Schubert
386*7f2fe78bSCy Schubert ret = alloc_data(rep, 6 + ap_rep.length + cipher.length);
387*7f2fe78bSCy Schubert if (ret)
388*7f2fe78bSCy Schubert goto bailout;
389*7f2fe78bSCy Schubert ptr = rep->data;
390*7f2fe78bSCy Schubert
391*7f2fe78bSCy Schubert /* length */
392*7f2fe78bSCy Schubert
393*7f2fe78bSCy Schubert *ptr++ = (rep->length>>8) & 0xff;
394*7f2fe78bSCy Schubert *ptr++ = rep->length & 0xff;
395*7f2fe78bSCy Schubert
396*7f2fe78bSCy Schubert /* version == 0x0001 big-endian */
397*7f2fe78bSCy Schubert
398*7f2fe78bSCy Schubert *ptr++ = 0;
399*7f2fe78bSCy Schubert *ptr++ = 1;
400*7f2fe78bSCy Schubert
401*7f2fe78bSCy Schubert /* ap_rep length, big-endian */
402*7f2fe78bSCy Schubert
403*7f2fe78bSCy Schubert *ptr++ = (ap_rep.length>>8) & 0xff;
404*7f2fe78bSCy Schubert *ptr++ = ap_rep.length & 0xff;
405*7f2fe78bSCy Schubert
406*7f2fe78bSCy Schubert /* ap-rep data */
407*7f2fe78bSCy Schubert
408*7f2fe78bSCy Schubert if (ap_rep.length) {
409*7f2fe78bSCy Schubert memcpy(ptr, ap_rep.data, ap_rep.length);
410*7f2fe78bSCy Schubert ptr += ap_rep.length;
411*7f2fe78bSCy Schubert }
412*7f2fe78bSCy Schubert
413*7f2fe78bSCy Schubert /* krb-priv or krb-error */
414*7f2fe78bSCy Schubert
415*7f2fe78bSCy Schubert memcpy(ptr, cipher.data, cipher.length);
416*7f2fe78bSCy Schubert
417*7f2fe78bSCy Schubert bailout:
418*7f2fe78bSCy Schubert krb5_auth_con_free(context, auth_context);
419*7f2fe78bSCy Schubert krb5_free_principal(context, changepw);
420*7f2fe78bSCy Schubert krb5_free_ticket(context, ticket);
421*7f2fe78bSCy Schubert free(ap_rep.data);
422*7f2fe78bSCy Schubert free(clear.data);
423*7f2fe78bSCy Schubert free(cipher.data);
424*7f2fe78bSCy Schubert krb5_free_principal(context, target);
425*7f2fe78bSCy Schubert krb5_free_unparsed_name(context, targetstr);
426*7f2fe78bSCy Schubert krb5_free_unparsed_name(context, clientstr);
427*7f2fe78bSCy Schubert krb5_free_error_message(context, errmsg);
428*7f2fe78bSCy Schubert return ret;
429*7f2fe78bSCy Schubert }
430*7f2fe78bSCy Schubert
431*7f2fe78bSCy Schubert /* Dispatch routine for set/change password */
432*7f2fe78bSCy Schubert void
dispatch(void * handle,const krb5_fulladdr * local_addr,const krb5_fulladdr * remote_addr,krb5_data * request,int is_tcp,verto_ctx * vctx,loop_respond_fn respond,void * arg)433*7f2fe78bSCy Schubert dispatch(void *handle, const krb5_fulladdr *local_addr,
434*7f2fe78bSCy Schubert const krb5_fulladdr *remote_addr, krb5_data *request, int is_tcp,
435*7f2fe78bSCy Schubert verto_ctx *vctx, loop_respond_fn respond, void *arg)
436*7f2fe78bSCy Schubert {
437*7f2fe78bSCy Schubert krb5_error_code ret;
438*7f2fe78bSCy Schubert krb5_keytab kt = NULL;
439*7f2fe78bSCy Schubert kadm5_server_handle_t server_handle = *(void **)handle;
440*7f2fe78bSCy Schubert krb5_data *response = NULL;
441*7f2fe78bSCy Schubert const char *emsg;
442*7f2fe78bSCy Schubert
443*7f2fe78bSCy Schubert ret = krb5_kt_resolve(server_handle->context, "KDB:", &kt);
444*7f2fe78bSCy Schubert if (ret != 0) {
445*7f2fe78bSCy Schubert emsg = krb5_get_error_message(server_handle->context, ret);
446*7f2fe78bSCy Schubert krb5_klog_syslog(LOG_ERR, _("chpw: Couldn't open admin keytab %s"),
447*7f2fe78bSCy Schubert emsg);
448*7f2fe78bSCy Schubert krb5_free_error_message(server_handle->context, emsg);
449*7f2fe78bSCy Schubert goto egress;
450*7f2fe78bSCy Schubert }
451*7f2fe78bSCy Schubert
452*7f2fe78bSCy Schubert response = k5alloc(sizeof(krb5_data), &ret);
453*7f2fe78bSCy Schubert if (response == NULL)
454*7f2fe78bSCy Schubert goto egress;
455*7f2fe78bSCy Schubert
456*7f2fe78bSCy Schubert ret = process_chpw_request(server_handle->context,
457*7f2fe78bSCy Schubert server_handle,
458*7f2fe78bSCy Schubert server_handle->params.realm,
459*7f2fe78bSCy Schubert kt,
460*7f2fe78bSCy Schubert local_addr,
461*7f2fe78bSCy Schubert remote_addr,
462*7f2fe78bSCy Schubert request,
463*7f2fe78bSCy Schubert response);
464*7f2fe78bSCy Schubert egress:
465*7f2fe78bSCy Schubert if (ret)
466*7f2fe78bSCy Schubert krb5_free_data(server_handle->context, response);
467*7f2fe78bSCy Schubert krb5_kt_close(server_handle->context, kt);
468*7f2fe78bSCy Schubert (*respond)(arg, ret, ret == 0 ? response : NULL);
469*7f2fe78bSCy Schubert }
470