8f1eb5377a4ac776f2308e1de4c79c808e007d0d
[lorikeet-heimdal.git] / kdc / process.c
1 /*
2  * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * 3. Neither the name of the Institute nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include "kdc_locl.h"
36 #include <vis.h>
37
38 /*
39  *
40  */
41
42 #undef  __attribute__
43 #define __attribute__(x)
44
45 KDC_LIB_FUNCTION void KDC_LIB_CALL
46 kdc_audit_vaddreason(kdc_request_t r, const char *fmt, va_list ap)
47         __attribute__ ((__format__ (__printf__, 2, 0)))
48 {
49     heim_audit_vaddreason((heim_svc_req_desc)r, fmt, ap);
50 }
51
52 KDC_LIB_FUNCTION void KDC_LIB_CALL
53 kdc_audit_addreason(kdc_request_t r, const char *fmt, ...)
54         __attribute__ ((__format__ (__printf__, 2, 3)))
55 {
56     va_list ap;
57
58     va_start(ap, fmt);
59     heim_audit_vaddreason((heim_svc_req_desc)r, fmt, ap);
60     va_end(ap);
61 }
62
63 /*
64  * append_token adds a token which is optionally a kv-pair and it
65  * also optionally eats the whitespace.  If k == NULL, then it's
66  * not a kv-pair.
67  */
68
69 KDC_LIB_FUNCTION void KDC_LIB_CALL
70 kdc_audit_vaddkv(kdc_request_t r, int flags, const char *k,
71                   const char *fmt, va_list ap)
72         __attribute__ ((__format__ (__printf__, 4, 0)))
73 {
74     heim_audit_vaddkv((heim_svc_req_desc)r, flags, k, fmt, ap);
75 }
76
77 KDC_LIB_FUNCTION void KDC_LIB_CALL
78 kdc_audit_addkv(kdc_request_t r, int flags, const char *k,
79                  const char *fmt, ...)
80         __attribute__ ((__format__ (__printf__, 4, 5)))
81 {
82     va_list ap;
83
84     va_start(ap, fmt);
85     heim_audit_vaddkv((heim_svc_req_desc)r, flags, k, fmt, ap);
86     va_end(ap);
87 }
88
89 KDC_LIB_FUNCTION void KDC_LIB_CALL
90 kdc_audit_addkv_timediff(kdc_request_t r, const char *k,
91                           const struct timeval *start,
92                           const struct timeval *end)
93 {
94     heim_audit_addkv_timediff((heim_svc_req_desc)r,k, start, end);
95 }
96
97 KDC_LIB_FUNCTION void KDC_LIB_CALL
98 kdc_audit_setkv_bool(kdc_request_t r, const char *k, krb5_boolean v)
99 {
100     heim_audit_setkv_bool((heim_svc_req_desc)r, k, (int)v);
101 }
102
103 KDC_LIB_FUNCTION void KDC_LIB_CALL
104 kdc_audit_addkv_number(kdc_request_t r, const char *k, int64_t v)
105 {
106     heim_audit_addkv_number((heim_svc_req_desc)r, k, v);
107 }
108
109 KDC_LIB_FUNCTION void KDC_LIB_CALL
110 kdc_audit_setkv_number(kdc_request_t r, const char *k, int64_t v)
111 {
112     heim_audit_setkv_number((heim_svc_req_desc)r, k, v);
113 }
114
115 KDC_LIB_FUNCTION void KDC_LIB_CALL
116 kdc_audit_addkv_object(kdc_request_t r, const char *k, kdc_object_t obj)
117 {
118     heim_audit_addkv_object((heim_svc_req_desc)r, k, obj);
119 }
120
121 KDC_LIB_FUNCTION void KDC_LIB_CALL
122 kdc_audit_setkv_object(kdc_request_t r, const char *k, kdc_object_t obj)
123 {
124     heim_audit_setkv_object((heim_svc_req_desc)r, k, obj);
125 }
126
127 KDC_LIB_FUNCTION kdc_object_t KDC_LIB_CALL
128 kdc_audit_getkv(kdc_request_t r, const char *k)
129 {
130     return heim_audit_getkv((heim_svc_req_desc)r, k);
131 }
132
133 /*
134  * Add up to 3 key value pairs to record HostAddresses from request body or
135  * PA-TGS ticket or whatever.
136  */
137 KDC_LIB_FUNCTION void KDC_LIB_CALL
138 kdc_audit_addaddrs(kdc_request_t r, HostAddresses *a, const char *key)
139 {
140     size_t i;
141     char buf[128];
142
143     if (a->len > 3) {
144         char numkey[32];
145
146         if (snprintf(numkey, sizeof(numkey), "num%s", key) >= sizeof(numkey))
147             numkey[31] = '\0';
148         kdc_audit_addkv(r, 0, numkey, "%llu", (unsigned long long)a->len);
149     }
150
151     for (i = 0; i < 3 && i < a->len; i++) {
152         if (krb5_print_address(&a->val[i], buf, sizeof(buf), NULL) == 0)
153             kdc_audit_addkv(r, 0, key, "%s", buf);
154     }
155 }
156
157 KDC_LIB_FUNCTION void KDC_LIB_CALL
158 _kdc_audit_trail(kdc_request_t r, krb5_error_code ret)
159 {
160     const char *retname = NULL;
161
162     /* Get a symbolic name for some error codes */
163 #define CASE(x) case x : retname = #x; break
164     switch (ret ? ret : r->error_code) {
165     CASE(ENOMEM);
166     CASE(EACCES);
167     CASE(HDB_ERR_NOT_FOUND_HERE);
168     CASE(HDB_ERR_WRONG_REALM);
169     CASE(HDB_ERR_EXISTS);
170     CASE(HDB_ERR_KVNO_NOT_FOUND);
171     CASE(HDB_ERR_NOENTRY);
172     CASE(HDB_ERR_NO_MKEY);
173     CASE(KRB5KDC_ERR_BADOPTION);
174     CASE(KRB5KDC_ERR_CANNOT_POSTDATE);
175     CASE(KRB5KDC_ERR_CLIENT_NOTYET);
176     CASE(KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN);
177     CASE(KRB5KDC_ERR_ETYPE_NOSUPP);
178     CASE(KRB5KDC_ERR_KEY_EXPIRED);
179     CASE(KRB5KDC_ERR_NAME_EXP);
180     CASE(KRB5KDC_ERR_NEVER_VALID);
181     CASE(KRB5KDC_ERR_NONE);
182     CASE(KRB5KDC_ERR_NULL_KEY);
183     CASE(KRB5KDC_ERR_PADATA_TYPE_NOSUPP);
184     CASE(KRB5KDC_ERR_POLICY);
185     CASE(KRB5KDC_ERR_PREAUTH_FAILED);
186     CASE(KRB5KDC_ERR_PREAUTH_REQUIRED);
187     CASE(KRB5KDC_ERR_SERVER_NOMATCH);
188     CASE(KRB5KDC_ERR_SERVICE_EXP);
189     CASE(KRB5KDC_ERR_SERVICE_NOTYET);
190     CASE(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
191     CASE(KRB5KDC_ERR_TRTYPE_NOSUPP);
192     CASE(KRB5KRB_AP_ERR_BADADDR);
193     CASE(KRB5KRB_AP_ERR_BADDIRECTION);
194     CASE(KRB5KRB_AP_ERR_BAD_INTEGRITY);
195     CASE(KRB5KRB_AP_ERR_BADKEYVER);
196     CASE(KRB5KRB_AP_ERR_BADMATCH);
197     CASE(KRB5KRB_AP_ERR_BADORDER);
198     CASE(KRB5KRB_AP_ERR_BADSEQ);
199     CASE(KRB5KRB_AP_ERR_BADVERSION);
200     CASE(KRB5KRB_AP_ERR_ILL_CR_TKT);
201     CASE(KRB5KRB_AP_ERR_INAPP_CKSUM);
202     CASE(KRB5KRB_AP_ERR_METHOD);
203     CASE(KRB5KRB_AP_ERR_MODIFIED);
204     CASE(KRB5KRB_AP_ERR_MSG_TYPE);
205     CASE(KRB5KRB_AP_ERR_MUT_FAIL);
206     CASE(KRB5KRB_AP_ERR_NOKEY);
207     CASE(KRB5KRB_AP_ERR_NOT_US);
208     CASE(KRB5KRB_AP_ERR_REPEAT);
209     CASE(KRB5KRB_AP_ERR_SKEW);
210     CASE(KRB5KRB_AP_ERR_TKT_EXPIRED);
211     CASE(KRB5KRB_AP_ERR_TKT_INVALID);
212     CASE(KRB5KRB_AP_ERR_TKT_NYV);
213     CASE(KRB5KRB_AP_ERR_V4_REPLY);
214     CASE(KRB5KRB_AP_PATH_NOT_ACCEPTED);
215     CASE(KRB5KRB_AP_WRONG_PRINC);
216     CASE(KRB5KRB_ERR_FIELD_TOOLONG);
217     CASE(KRB5KRB_ERR_GENERIC);
218     CASE(KRB5KRB_ERR_RESPONSE_TOO_BIG);
219
220     case 0:
221         retname = "SUCCESS";
222         break;
223     default:
224         retname = NULL;
225         break;
226     }
227
228     /* Let's save a few bytes */
229 #define PREFIX "KRB5KDC_"
230     if (retname && strncmp(PREFIX, retname, strlen(PREFIX)) == 0)
231         retname += strlen(PREFIX);
232 #undef PREFIX
233
234     heim_audit_trail((heim_svc_req_desc)r, ret, retname);
235 }
236
237 KDC_LIB_FUNCTION void KDC_LIB_CALL
238 krb5_kdc_update_time(struct timeval *tv)
239 {
240     if (tv == NULL)
241         gettimeofday(&_kdc_now, NULL);
242     else
243         _kdc_now = *tv;
244 }
245
246
247 #define EXTEND_REQUEST_T(LHS, RHS) do {                 \
248         RHS = realloc(LHS, sizeof(*RHS));               \
249         if (!RHS)                                       \
250             return krb5_enomem((LHS)->context);         \
251         LHS = (void *)RHS;                              \
252         memset(((char *)LHS) + sizeof(*LHS),            \
253                0x0,                                     \
254                sizeof(*RHS) - sizeof(*LHS));            \
255     } while (0)
256
257 static krb5_error_code
258 kdc_as_req(kdc_request_t *rptr, int *claim)
259 {
260     astgs_request_t r;
261     krb5_error_code ret;
262     size_t len;
263
264     /* We must free things in the extensions */
265     EXTEND_REQUEST_T(*rptr, r);
266
267     ret = decode_AS_REQ(r->request.data, r->request.length, &r->req, &len);
268     if (ret)
269         return ret;
270
271     r->reqtype = "AS-REQ";
272     r->use_request_t = 1;
273     *claim = 1;
274
275     ret = _kdc_as_rep(r);
276     free_AS_REQ(&r->req);
277     return ret;
278 }
279
280
281 static krb5_error_code
282 kdc_tgs_req(kdc_request_t *rptr, int *claim)
283 {
284     astgs_request_t r;
285     krb5_error_code ret;
286     size_t len;
287
288     /* We must free things in the extensions */
289     EXTEND_REQUEST_T(*rptr, r);
290
291     ret = decode_TGS_REQ(r->request.data, r->request.length, &r->req, &len);
292     if (ret)
293         return ret;
294
295     r->reqtype = "TGS-REQ";
296     r->use_request_t = 1;
297     *claim = 1;
298
299     ret = _kdc_tgs_rep(r);
300     free_TGS_REQ(&r->req);
301     return ret;
302 }
303
304 #ifdef DIGEST
305
306 static krb5_error_code
307 kdc_digest(kdc_request_t *rptr, int *claim)
308 {
309     kdc_request_t r;
310     DigestREQ digestreq;
311     krb5_error_code ret;
312     size_t len;
313
314     r = *rptr;
315
316     ret = decode_DigestREQ(r->request.data, r->request.length,
317                            &digestreq, &len);
318     if (ret)
319         return ret;
320
321     r->use_request_t = 0;
322     *claim = 1;
323
324     ret = _kdc_do_digest(r->context, r->config, &digestreq,
325                          r->reply, r->from, r->addr);
326     free_DigestREQ(&digestreq);
327     return ret;
328 }
329
330 #endif
331
332 #ifdef KX509
333
334 static krb5_error_code
335 kdc_kx509(kdc_request_t *rptr, int *claim)
336 {
337     kx509_req_context r;
338     krb5_error_code ret;
339
340     /* We must free things in the extensions */
341     EXTEND_REQUEST_T(*rptr, r);
342
343     ret = _kdc_try_kx509_request(r);
344     if (ret)
345         return ret;
346
347     r->use_request_t = 1;
348     r->reqtype = "KX509";
349     *claim = 1;
350
351     return _kdc_do_kx509(r); /* Must clean up the req struct extensions */
352 }
353
354 #endif
355
356
357 static struct krb5_kdc_service services[] =  {
358     { KS_KRB5, "AS-REQ",        kdc_as_req },
359     { KS_KRB5, "TGS-REQ",       kdc_tgs_req },
360 #ifdef DIGEST
361     { 0,        "DIGEST",       kdc_digest },
362 #endif
363 #ifdef KX509
364     { 0,        "KX509",        kdc_kx509 },
365 #endif
366     { 0, NULL, NULL }
367 };
368
369 static int
370 process_request(krb5_context context,
371                 krb5_kdc_configuration *config,
372                 unsigned int krb5_only,
373                 unsigned char *buf,
374                 size_t len,
375                 krb5_data *reply,
376                 krb5_boolean *prependlength,
377                 const char *from,
378                 struct sockaddr *addr,
379                 int datagram_reply)
380 {
381     kdc_request_t r;
382     krb5_error_code ret;
383     unsigned int i;
384     int claim = 0;
385
386     r = calloc(sizeof(*r), 1);
387     if (!r)
388         return krb5_enomem(context);
389
390     r->context = context;
391     r->hcontext = context->hcontext;
392     r->config = config;
393     r->logf = config->logf;
394     r->from = from;
395     r->addr = addr;
396     r->request.data = buf;
397     r->request.length = len;
398     r->datagram_reply = datagram_reply;
399     r->reply = reply;
400     r->kv = heim_dict_create(10);
401     r->attributes = heim_dict_create(1);
402     if (r->kv == NULL || r->attributes == NULL) {
403         heim_release(r->kv);
404         heim_release(r->attributes);
405         free(r);
406         return krb5_enomem(context);
407     }
408
409     gettimeofday(&r->tv_start, NULL);
410
411     for (i = 0; services[i].process != NULL; i++) {
412         if (krb5_only && (services[i].flags & KS_KRB5) == 0)
413             continue;
414         kdc_log(context, config, 7, "Probing for %s", services[i].name);
415         ret = (*services[i].process)(&r, &claim);
416         if (claim) {
417             if (prependlength && services[i].flags & KS_NO_LENGTH)
418                 *prependlength = 0;
419
420             if (r->use_request_t) {
421                 gettimeofday(&r->tv_end, NULL);
422                 _kdc_audit_trail(r, ret);
423                 free(r->cname);
424                 free(r->sname);
425                 free(r->e_text_buf);
426             }
427
428             heim_release(r->reason);
429             heim_release(r->kv);
430             heim_release(r->attributes);
431             free(r);
432             return ret;
433         }
434     }
435
436     heim_release(r->reason);
437     heim_release(r->kv);
438     heim_release(r->attributes);
439     free(r);
440     return -1;
441 }
442
443 /*
444  * handle the request in `buf, len', from `addr' (or `from' as a string),
445  * sending a reply in `reply'.
446  */
447
448 KDC_LIB_FUNCTION int KDC_LIB_CALL
449 krb5_kdc_process_request(krb5_context context,
450                          krb5_kdc_configuration *config,
451                          unsigned char *buf,
452                          size_t len,
453                          krb5_data *reply,
454                          krb5_boolean *prependlength,
455                          const char *from,
456                          struct sockaddr *addr,
457                          int datagram_reply)
458 {
459     return process_request(context, config, 0, buf, len, reply, prependlength,
460                            from, addr, datagram_reply);
461 }
462  
463 /*
464  * handle the request in `buf, len', from `addr' (or `from' as a string),
465  * sending a reply in `reply'.
466  *
467  * This only processes krb5 requests
468  */
469
470 KDC_LIB_FUNCTION int KDC_LIB_CALL
471 krb5_kdc_process_krb5_request(krb5_context context,
472                               krb5_kdc_configuration *config,
473                               unsigned char *buf,
474                               size_t len,
475                               krb5_data *reply,
476                               const char *from,
477                               struct sockaddr *addr,
478                               int datagram_reply)
479 {
480     return process_request(context, config, 1, buf, len, reply, NULL,
481                            from, addr, datagram_reply);
482 }
483
484
485 /*
486  *
487  */
488
489 KDC_LIB_FUNCTION int KDC_LIB_CALL
490 krb5_kdc_save_request(krb5_context context,
491                       const char *fn,
492                       const unsigned char *buf,
493                       size_t len,
494                       const krb5_data *reply,
495                       const struct sockaddr *sa)
496 {
497     krb5_storage *sp;
498     krb5_address a;
499     int fd = -1;
500     int ret = 0;
501     uint32_t t;
502     krb5_data d;
503
504     memset(&a, 0, sizeof(a));
505
506     d.data = rk_UNCONST(buf); /* do not free here */
507     d.length = len;
508     t = _kdc_now.tv_sec;
509
510     sp = krb5_storage_emem();
511     if (sp == NULL)
512         ret = krb5_enomem(context);
513
514     if (ret == 0)
515         ret = krb5_sockaddr2address(context, sa, &a);
516     if (ret == 0)
517         ret = krb5_store_uint32(sp, 1);
518     if (ret == 0)
519         ret = krb5_store_uint32(sp, t);
520     if (ret == 0)
521         ret = krb5_store_address(sp, a);
522     if (ret == 0)
523         ret = krb5_store_data(sp, d);
524     d.length = 0;
525     d.data = NULL;
526     if (ret == 0) {
527         Der_class cl;
528         Der_type ty;
529         unsigned int tag;
530         ret = der_get_tag (reply->data, reply->length,
531                            &cl, &ty, &tag, NULL);
532         if (ret) {
533             ret = krb5_store_uint32(sp, 0xffffffff);
534             if (ret == 0)
535                 ret = krb5_store_uint32(sp, 0xffffffff);
536         } else {
537             ret = krb5_store_uint32(sp, MAKE_TAG(cl, ty, 0));
538             if (ret == 0)
539                 ret = krb5_store_uint32(sp, tag);
540         }
541     }
542
543     if (ret == 0)
544         ret = krb5_storage_to_data(sp, &d);
545     krb5_storage_free(sp);
546     sp = NULL;
547
548     /*
549      * We've got KDC concurrency, so we're going to try to do a single O_APPEND
550      * write(2).  Hopefully we manage to write enough of the header that one
551      * can skip this request if it fails to write completely.
552      */
553     if (ret == 0)
554         fd = open(fn, O_WRONLY|O_CREAT|O_APPEND, 0600);
555     if (fd < 0)
556         krb5_set_error_message(context, ret = errno, "Failed to open: %s", fn);
557     if (ret == 0) {
558         sp = krb5_storage_from_fd(fd);
559         if (sp == NULL)
560             krb5_set_error_message(context, ret = ENOMEM,
561                                    "Storage failed to open fd");
562     }
563     (void) close(fd);
564     if (ret == 0)
565         ret = krb5_store_data(sp, d);
566     krb5_free_address(context, &a);
567     /*
568      * krb5_storage_free() currently always returns 0, but for FDs it sets
569      * errno to whatever close() set it to if it failed.
570      */
571     errno = 0;
572     if (ret == 0)
573         ret = krb5_storage_free(sp);
574     else
575         (void) krb5_storage_free(sp);
576     if (ret == 0 && errno)
577         ret = errno;
578
579     return ret;
580 }
581
582 KDC_LIB_FUNCTION krb5_error_code KDC_LIB_CALL
583 kdc_request_set_attribute(kdc_request_t r, kdc_object_t key, kdc_object_t value)
584 {
585     return heim_dict_set_value(r->attributes, key, value);
586 }
587
588 KDC_LIB_FUNCTION kdc_object_t KDC_LIB_CALL
589 kdc_request_get_attribute(kdc_request_t r, kdc_object_t key)
590 {
591     return heim_dict_get_value(r->attributes, key);
592 }
593
594 KDC_LIB_FUNCTION kdc_object_t KDC_LIB_CALL
595 kdc_request_copy_attribute(kdc_request_t r, kdc_object_t key)
596 {
597     return heim_dict_copy_value(r->attributes, key);
598 }
599
600 KDC_LIB_FUNCTION void KDC_LIB_CALL
601 kdc_request_delete_attribute(kdc_request_t r, kdc_object_t key)
602 {
603     heim_dict_delete_key(r->attributes, key);
604 }