2 * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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.
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.
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
43 #define __attribute__(x)
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)))
49 heim_audit_vaddreason((heim_svc_req_desc
)r
, fmt
, ap
);
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)))
59 heim_audit_vaddreason((heim_svc_req_desc
)r
, fmt
, ap
);
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
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)))
74 heim_audit_vaddkv((heim_svc_req_desc
)r
, flags
, k
, fmt
, ap
);
77 KDC_LIB_FUNCTION
void KDC_LIB_CALL
78 kdc_audit_addkv(kdc_request_t r
, int flags
, const char *k
,
80 __attribute__ ((__format__ (__printf__
, 4, 5)))
85 heim_audit_vaddkv((heim_svc_req_desc
)r
, flags
, k
, fmt
, ap
);
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
)
94 heim_audit_addkv_timediff((heim_svc_req_desc
)r
,k
, start
, end
);
97 KDC_LIB_FUNCTION
void KDC_LIB_CALL
98 kdc_audit_setkv_bool(kdc_request_t r
, const char *k
, krb5_boolean v
)
100 heim_audit_setkv_bool((heim_svc_req_desc
)r
, k
, (int)v
);
103 KDC_LIB_FUNCTION
void KDC_LIB_CALL
104 kdc_audit_addkv_number(kdc_request_t r
, const char *k
, int64_t v
)
106 heim_audit_addkv_number((heim_svc_req_desc
)r
, k
, v
);
109 KDC_LIB_FUNCTION
void KDC_LIB_CALL
110 kdc_audit_setkv_number(kdc_request_t r
, const char *k
, int64_t v
)
112 heim_audit_setkv_number((heim_svc_req_desc
)r
, k
, v
);
115 KDC_LIB_FUNCTION
void KDC_LIB_CALL
116 kdc_audit_addkv_object(kdc_request_t r
, const char *k
, kdc_object_t obj
)
118 heim_audit_addkv_object((heim_svc_req_desc
)r
, k
, obj
);
121 KDC_LIB_FUNCTION
void KDC_LIB_CALL
122 kdc_audit_setkv_object(kdc_request_t r
, const char *k
, kdc_object_t obj
)
124 heim_audit_setkv_object((heim_svc_req_desc
)r
, k
, obj
);
127 KDC_LIB_FUNCTION kdc_object_t KDC_LIB_CALL
128 kdc_audit_getkv(kdc_request_t r
, const char *k
)
130 return heim_audit_getkv((heim_svc_req_desc
)r
, k
);
134 * Add up to 3 key value pairs to record HostAddresses from request body or
135 * PA-TGS ticket or whatever.
137 KDC_LIB_FUNCTION
void KDC_LIB_CALL
138 kdc_audit_addaddrs(kdc_request_t r
, HostAddresses
*a
, const char *key
)
146 if (snprintf(numkey
, sizeof(numkey
), "num%s", key
) >= sizeof(numkey
))
148 kdc_audit_addkv(r
, 0, numkey
, "%llu", (unsigned long long)a
->len
);
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
);
157 KDC_LIB_FUNCTION
void KDC_LIB_CALL
158 _kdc_audit_trail(kdc_request_t r
, krb5_error_code ret
)
160 const char *retname
= NULL
;
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
) {
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
);
228 /* Let's save a few bytes */
229 #define PREFIX "KRB5KDC_"
230 if (retname
&& strncmp(PREFIX
, retname
, strlen(PREFIX
)) == 0)
231 retname
+= strlen(PREFIX
);
234 heim_audit_trail((heim_svc_req_desc
)r
, ret
, retname
);
237 KDC_LIB_FUNCTION
void KDC_LIB_CALL
238 krb5_kdc_update_time(struct timeval
*tv
)
241 gettimeofday(&_kdc_now
, NULL
);
246 KDC_LIB_FUNCTION
struct timeval KDC_LIB_CALL
247 krb5_kdc_get_time(void)
253 #define EXTEND_REQUEST_T(LHS, RHS) do { \
254 RHS = realloc(LHS, sizeof(*RHS)); \
256 return krb5_enomem((LHS)->context); \
258 memset(((char *)LHS) + sizeof(*LHS), \
260 sizeof(*RHS) - sizeof(*LHS)); \
263 static krb5_error_code
264 kdc_as_req(kdc_request_t
*rptr
, int *claim
)
270 /* We must free things in the extensions */
271 EXTEND_REQUEST_T(*rptr
, r
);
273 ret
= decode_AS_REQ(r
->request
.data
, r
->request
.length
, &r
->req
, &len
);
277 r
->reqtype
= "AS-REQ";
278 r
->use_request_t
= 1;
281 ret
= _kdc_as_rep(r
);
282 free_AS_REQ(&r
->req
);
287 static krb5_error_code
288 kdc_tgs_req(kdc_request_t
*rptr
, int *claim
)
294 /* We must free things in the extensions */
295 EXTEND_REQUEST_T(*rptr
, r
);
297 ret
= decode_TGS_REQ(r
->request
.data
, r
->request
.length
, &r
->req
, &len
);
301 r
->reqtype
= "TGS-REQ";
302 r
->use_request_t
= 1;
305 ret
= _kdc_tgs_rep(r
);
306 free_TGS_REQ(&r
->req
);
312 static krb5_error_code
313 kdc_digest(kdc_request_t
*rptr
, int *claim
)
322 ret
= decode_DigestREQ(r
->request
.data
, r
->request
.length
,
327 r
->use_request_t
= 0;
330 ret
= _kdc_do_digest(r
->context
, r
->config
, &digestreq
,
331 r
->reply
, r
->from
, r
->addr
);
332 free_DigestREQ(&digestreq
);
340 static krb5_error_code
341 kdc_kx509(kdc_request_t
*rptr
, int *claim
)
346 /* We must free things in the extensions */
347 EXTEND_REQUEST_T(*rptr
, r
);
349 ret
= _kdc_try_kx509_request(r
);
353 r
->use_request_t
= 1;
354 r
->reqtype
= "KX509";
357 return _kdc_do_kx509(r
); /* Must clean up the req struct extensions */
363 static struct krb5_kdc_service services
[] = {
364 { KS_KRB5
, "AS-REQ", kdc_as_req
},
365 { KS_KRB5
, "TGS-REQ", kdc_tgs_req
},
367 { 0, "DIGEST", kdc_digest
},
370 { 0, "KX509", kdc_kx509
},
376 process_request(krb5_context context
,
377 krb5_kdc_configuration
*config
,
378 unsigned int krb5_only
,
382 krb5_boolean
*prependlength
,
384 struct sockaddr
*addr
,
392 r
= calloc(1, sizeof(*r
));
394 return krb5_enomem(context
);
396 r
->context
= context
;
397 r
->hcontext
= context
->hcontext
;
399 r
->logf
= config
->logf
;
402 r
->request
.data
= buf
;
403 r
->request
.length
= len
;
404 r
->datagram_reply
= datagram_reply
;
406 r
->kv
= heim_dict_create(10);
407 r
->attributes
= heim_dict_create(1);
408 if (r
->kv
== NULL
|| r
->attributes
== NULL
) {
410 heim_release(r
->attributes
);
412 return krb5_enomem(context
);
415 gettimeofday(&r
->tv_start
, NULL
);
417 for (i
= 0; services
[i
].process
!= NULL
; i
++) {
418 if (krb5_only
&& (services
[i
].flags
& KS_KRB5
) == 0)
420 kdc_log(context
, config
, 7, "Probing for %s", services
[i
].name
);
421 ret
= (*services
[i
].process
)(&r
, &claim
);
423 if (prependlength
&& services
[i
].flags
& KS_NO_LENGTH
)
426 if (r
->use_request_t
) {
427 gettimeofday(&r
->tv_end
, NULL
);
428 _kdc_audit_trail(r
, ret
);
432 krb5_data_free(&r
->e_data
);
435 heim_release(r
->reason
);
437 heim_release(r
->attributes
);
443 heim_release(r
->reason
);
445 heim_release(r
->attributes
);
451 * handle the request in `buf, len', from `addr' (or `from' as a string),
452 * sending a reply in `reply'.
455 KDC_LIB_FUNCTION
int KDC_LIB_CALL
456 krb5_kdc_process_request(krb5_context context
,
457 krb5_kdc_configuration
*config
,
461 krb5_boolean
*prependlength
,
463 struct sockaddr
*addr
,
466 return process_request(context
, config
, 0, buf
, len
, reply
, prependlength
,
467 from
, addr
, datagram_reply
);
471 * handle the request in `buf, len', from `addr' (or `from' as a string),
472 * sending a reply in `reply'.
474 * This only processes krb5 requests
477 KDC_LIB_FUNCTION
int KDC_LIB_CALL
478 krb5_kdc_process_krb5_request(krb5_context context
,
479 krb5_kdc_configuration
*config
,
484 struct sockaddr
*addr
,
487 return process_request(context
, config
, 1, buf
, len
, reply
, NULL
,
488 from
, addr
, datagram_reply
);
496 KDC_LIB_FUNCTION
int KDC_LIB_CALL
497 krb5_kdc_save_request(krb5_context context
,
499 const unsigned char *buf
,
501 const krb5_data
*reply
,
502 const struct sockaddr
*sa
)
511 memset(&a
, 0, sizeof(a
));
513 d
.data
= rk_UNCONST(buf
); /* do not free here */
517 sp
= krb5_storage_emem();
519 ret
= krb5_enomem(context
);
522 ret
= krb5_sockaddr2address(context
, sa
, &a
);
524 ret
= krb5_store_uint32(sp
, 1);
526 ret
= krb5_store_uint32(sp
, t
);
528 ret
= krb5_store_address(sp
, a
);
530 ret
= krb5_store_data(sp
, d
);
537 ret
= der_get_tag (reply
->data
, reply
->length
,
538 &cl
, &ty
, &tag
, NULL
);
540 ret
= krb5_store_uint32(sp
, 0xffffffff);
542 ret
= krb5_store_uint32(sp
, 0xffffffff);
544 ret
= krb5_store_uint32(sp
, MAKE_TAG(cl
, ty
, 0));
546 ret
= krb5_store_uint32(sp
, tag
);
551 ret
= krb5_storage_to_data(sp
, &d
);
552 krb5_storage_free(sp
);
556 * We've got KDC concurrency, so we're going to try to do a single O_APPEND
557 * write(2). Hopefully we manage to write enough of the header that one
558 * can skip this request if it fails to write completely.
561 fd
= open(fn
, O_WRONLY
|O_CREAT
|O_APPEND
, 0600);
563 krb5_set_error_message(context
, ret
= errno
, "Failed to open: %s", fn
);
565 sp
= krb5_storage_from_fd(fd
);
567 krb5_set_error_message(context
, ret
= ENOMEM
,
568 "Storage failed to open fd");
572 ret
= krb5_store_data(sp
, d
);
573 krb5_free_address(context
, &a
);
575 * krb5_storage_free() currently always returns 0, but for FDs it sets
576 * errno to whatever close() set it to if it failed.
580 ret
= krb5_storage_free(sp
);
582 (void) krb5_storage_free(sp
);
583 if (ret
== 0 && errno
)
589 KDC_LIB_FUNCTION krb5_error_code KDC_LIB_CALL
590 kdc_request_set_attribute(kdc_request_t r
, kdc_object_t key
, kdc_object_t value
)
592 return heim_dict_set_value(r
->attributes
, key
, value
);
595 KDC_LIB_FUNCTION kdc_object_t KDC_LIB_CALL
596 kdc_request_get_attribute(kdc_request_t r
, kdc_object_t key
)
598 return heim_dict_get_value(r
->attributes
, key
);
601 KDC_LIB_FUNCTION kdc_object_t KDC_LIB_CALL
602 kdc_request_copy_attribute(kdc_request_t r
, kdc_object_t key
)
604 return heim_dict_copy_value(r
->attributes
, key
);
607 KDC_LIB_FUNCTION
void KDC_LIB_CALL
608 kdc_request_delete_attribute(kdc_request_t r
, kdc_object_t key
)
610 heim_dict_delete_key(r
->attributes
, key
);