xref: /freebsd/crypto/krb5/src/kadmin/server/schpw.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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