2 * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "kadmin_locl.h"
35 #include <krb5-private.h>
37 static kadm5_ret_t
check_aliases(kadm5_server_context
*,
38 kadm5_principal_ent_rec
*,
39 kadm5_principal_ent_rec
*);
42 kadmind_dispatch(void *kadm_handlep
, krb5_boolean initial
,
43 krb5_data
*in
, krb5_data
*out
)
46 int32_t cmd
, mask
, tmp
;
47 kadm5_server_context
*contextp
= kadm_handlep
;
48 char client
[128], name
[128], name2
[128];
50 krb5_principal princ
, princ2
;
51 kadm5_principal_ent_rec ent
, ent_prev
;
52 char *password
, *expression
;
53 krb5_keyblock
*new_keys
;
54 krb5_key_salt_tuple
*ks_tuple
= NULL
;
55 krb5_boolean keepold
= FALSE
;
63 krb5_unparse_name_fixed(contextp
->context
, contextp
->caller
,
64 client
, sizeof(client
));
66 sp
= krb5_storage_from_data(in
);
68 krb5_errx(contextp
->context
, 1, "out of memory");
70 krb5_ret_int32(sp
, &cmd
);
74 ret
= krb5_ret_principal(sp
, &princ
);
77 ret
= krb5_ret_int32(sp
, &mask
);
79 krb5_free_principal(contextp
->context
, princ
);
82 mask
|= KADM5_PRINCIPAL
;
83 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
84 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
86 /* If the caller doesn't have KADM5_PRIV_GET, we're done. */
87 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_GET
, princ
);
89 krb5_free_principal(contextp
->context
, princ
);
93 /* Then check to see if it is ok to return keys */
94 if ((mask
& KADM5_KEY_DATA
) != 0) {
95 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_GET_KEYS
,
99 } else if ((mask
== (KADM5_PRINCIPAL
|KADM5_KEY_DATA
)) ||
100 (mask
== (KADM5_PRINCIPAL
|KADM5_KVNO
|KADM5_KEY_DATA
))) {
102 * Requests for keys will get bogus keys, which is useful if
103 * the client just wants to see what (kvno, enctype)s the
104 * principal has keys for, but terrible if the client wants to
105 * write the keys into a keytab or modify the principal and
106 * write the bogus keys back to the server.
108 * We use a heuristic to detect which case we're handling here.
109 * If the client only asks for the flags in the above
110 * condition, then it's very likely a kadmin ext_keytab,
111 * add_enctype, or other request that should not see bogus
112 * keys. We deny them.
114 * The kadmin get command can be coaxed into making a request
115 * with the same mask. But the default long and terse output
116 * modes request other things too, so in all likelihood this
117 * heuristic will not hurt any kadmin get uses.
119 krb5_free_principal(contextp
->context
, princ
);
124 ret
= kadm5_get_principal(kadm_handlep
, princ
, &ent
, mask
);
125 krb5_storage_free(sp
);
126 sp
= krb5_storage_emem();
127 krb5_store_int32(sp
, ret
);
130 kadm5_store_principal_ent(sp
, &ent
);
132 kadm5_store_principal_ent_nokeys(sp
, &ent
);
133 kadm5_free_principal_ent(kadm_handlep
, &ent
);
135 krb5_free_principal(contextp
->context
, princ
);
140 ret
= krb5_ret_principal(sp
, &princ
);
143 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
144 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
145 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_DELETE
, princ
);
147 krb5_free_principal(contextp
->context
, princ
);
152 * There's no need to check that the caller has permission to
153 * delete the victim principal's aliases.
156 ret
= kadm5_delete_principal(kadm_handlep
, princ
);
157 krb5_free_principal(contextp
->context
, princ
);
158 krb5_storage_free(sp
);
159 sp
= krb5_storage_emem();
160 krb5_store_int32(sp
, ret
);
165 ret
= kadm5_ret_principal_ent(sp
, &ent
);
168 ret
= krb5_ret_int32(sp
, &mask
);
170 kadm5_free_principal_ent(kadm_handlep
, &ent
);
173 ret
= krb5_ret_string(sp
, &password
);
175 kadm5_free_principal_ent(kadm_handlep
, &ent
);
178 krb5_unparse_name_fixed(contextp
->context
, ent
.principal
,
180 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
181 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_ADD
,
184 kadm5_free_principal_ent(kadm_handlep
, &ent
);
185 memset(password
, 0, strlen(password
));
189 if ((mask
& KADM5_TL_DATA
)) {
191 * Also check that the caller can create the aliases, if the
192 * new principal has any.
194 ret
= check_aliases(contextp
, &ent
, NULL
);
196 kadm5_free_principal_ent(kadm_handlep
, &ent
);
197 memset(password
, 0, strlen(password
));
202 ret
= kadm5_create_principal(kadm_handlep
, &ent
,
204 kadm5_free_principal_ent(kadm_handlep
, &ent
);
205 memset(password
, 0, strlen(password
));
207 krb5_storage_free(sp
);
208 sp
= krb5_storage_emem();
209 krb5_store_int32(sp
, ret
);
214 ret
= kadm5_ret_principal_ent(sp
, &ent
);
217 ret
= krb5_ret_int32(sp
, &mask
);
219 kadm5_free_principal_ent(contextp
, &ent
);
222 krb5_unparse_name_fixed(contextp
->context
, ent
.principal
,
224 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
225 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_MODIFY
,
228 kadm5_free_principal_ent(contextp
, &ent
);
231 if ((mask
& KADM5_TL_DATA
)) {
233 * Also check that the caller can create aliases that are in
234 * the new entry but not the old one. There's no need to
235 * check that the caller can delete aliases it wants to
236 * drop. See also handling of rename.
238 ret
= kadm5_get_principal(kadm_handlep
, ent
.principal
, &ent_prev
, mask
);
240 kadm5_free_principal_ent(contextp
, &ent
);
243 ret
= check_aliases(contextp
, &ent
, &ent_prev
);
244 kadm5_free_principal_ent(contextp
, &ent_prev
);
246 kadm5_free_principal_ent(contextp
, &ent
);
250 ret
= kadm5_modify_principal(kadm_handlep
, &ent
, mask
);
251 kadm5_free_principal_ent(kadm_handlep
, &ent
);
252 krb5_storage_free(sp
);
253 sp
= krb5_storage_emem();
254 krb5_store_int32(sp
, ret
);
259 ret
= krb5_ret_principal(sp
, &princ
);
262 ret
= krb5_ret_principal(sp
, &princ2
);
264 krb5_free_principal(contextp
->context
, princ
);
267 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
268 krb5_unparse_name_fixed(contextp
->context
, princ2
,
269 name2
, sizeof(name2
));
270 krb5_warnx(contextp
->context
, "%s: %s %s -> %s",
271 client
, op
, name
, name2
);
272 ret
= _kadm5_acl_check_permission(contextp
,
277 * Also require modify for the principal. For backwards
278 * compatibility, allow delete permission on the old name to
279 * cure lack of modify permission on the old name.
281 ret
= _kadm5_acl_check_permission(contextp
,
285 ret
= _kadm5_acl_check_permission(contextp
,
291 krb5_free_principal(contextp
->context
, princ
);
292 krb5_free_principal(contextp
->context
, princ2
);
295 ret
= kadm5_rename_principal(kadm_handlep
, princ
, princ2
);
296 krb5_free_principal(contextp
->context
, princ
);
297 krb5_free_principal(contextp
->context
, princ2
);
298 krb5_storage_free(sp
);
299 sp
= krb5_storage_emem();
300 krb5_store_int32(sp
, ret
);
305 ret
= krb5_ret_principal(sp
, &princ
);
308 ret
= krb5_ret_string(sp
, &password
);
310 krb5_free_principal(contextp
->context
, princ
);
313 ret
= krb5_ret_int32(sp
, &keepold
);
314 if (ret
&& ret
!= HEIM_ERR_EOF
) {
315 krb5_free_principal(contextp
->context
, princ
);
318 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
319 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
322 * The change is allowed if at least one of:
324 * a) allowed by sysadmin
325 * b) it's for the principal him/herself and this was an
326 * initial ticket, but then, check with the password quality
328 * c) the user is on the CPW ACL.
331 if (krb5_config_get_bool_default(contextp
->context
, NULL
, TRUE
,
332 "kadmin", "allow_self_change_password", NULL
)
334 && krb5_principal_compare (contextp
->context
, contextp
->caller
,
338 const char *pwd_reason
;
340 pwd_data
.data
= password
;
341 pwd_data
.length
= strlen(password
);
343 pwd_reason
= kadm5_check_password_quality (contextp
->context
,
345 if (pwd_reason
!= NULL
)
346 ret
= KADM5_PASS_Q_DICT
;
350 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_CPW
, princ
);
353 krb5_free_principal(contextp
->context
, princ
);
354 memset(password
, 0, strlen(password
));
358 ret
= kadm5_chpass_principal_3(kadm_handlep
, princ
, keepold
, 0, NULL
,
360 krb5_free_principal(contextp
->context
, princ
);
361 memset(password
, 0, strlen(password
));
363 krb5_storage_free(sp
);
364 sp
= krb5_storage_emem();
365 krb5_store_int32(sp
, ret
);
368 case kadm_chpass_with_key
:{
370 krb5_key_data
*key_data
;
373 op
= "CHPASS_WITH_KEY";
374 ret
= krb5_ret_principal(sp
, &princ
);
377 ret
= krb5_ret_int32(sp
, &n_key_data
);
379 krb5_free_principal(contextp
->context
, princ
);
382 ret
= krb5_ret_int32(sp
, &keepold
);
383 if (ret
&& ret
!= HEIM_ERR_EOF
) {
384 krb5_free_principal(contextp
->context
, princ
);
387 /* n_key_data will be squeezed into an int16_t below. */
388 if (n_key_data
< 0 || n_key_data
>= 1 << 16 ||
389 (size_t)n_key_data
> UINT_MAX
/sizeof(*key_data
)) {
391 krb5_free_principal(contextp
->context
, princ
);
395 key_data
= malloc (n_key_data
* sizeof(*key_data
));
396 if (key_data
== NULL
&& n_key_data
!= 0) {
398 krb5_free_principal(contextp
->context
, princ
);
402 for (i
= 0; i
< n_key_data
; ++i
) {
403 ret
= kadm5_ret_key_data (sp
, &key_data
[i
]);
407 kadm5_free_key_data (contextp
, &dummy
, key_data
);
409 krb5_free_principal(contextp
->context
, princ
);
414 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
415 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
418 * The change is only allowed if the user is on the CPW ACL,
419 * this it to force password quality check on the user.
422 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_CPW
, princ
);
424 int16_t dummy
= n_key_data
;
426 kadm5_free_key_data (contextp
, &dummy
, key_data
);
428 krb5_free_principal(contextp
->context
, princ
);
431 ret
= kadm5_chpass_principal_with_key_3(kadm_handlep
, princ
, keepold
,
432 n_key_data
, key_data
);
434 int16_t dummy
= n_key_data
;
435 kadm5_free_key_data (contextp
, &dummy
, key_data
);
438 krb5_free_principal(contextp
->context
, princ
);
439 krb5_storage_free(sp
);
440 sp
= krb5_storage_emem();
441 krb5_store_int32(sp
, ret
);
446 ret
= krb5_ret_principal(sp
, &princ
);
449 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
450 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
452 * The change is allowed if at least one of:
453 * a) it's for the principal him/herself and this was an initial ticket
454 * b) the user is on the CPW ACL.
458 && krb5_principal_compare (contextp
->context
, contextp
->caller
,
462 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_CPW
, princ
);
465 krb5_free_principal(contextp
->context
, princ
);
470 * See comments in kadm5_c_randkey_principal() regarding the
473 ret
= krb5_ret_int32(sp
, &keepold
);
474 if (ret
!= 0 && ret
!= HEIM_ERR_EOF
) {
475 krb5_free_principal(contextp
->context
, princ
);
479 ret
= krb5_ret_int32(sp
, &n_ks_tuple
);
480 if (ret
!= 0 && ret
!= HEIM_ERR_EOF
) {
481 krb5_free_principal(contextp
->context
, princ
);
483 } else if (ret
== 0) {
486 if (n_ks_tuple
< 0) {
488 krb5_free_principal(contextp
->context
, princ
);
492 if ((ks_tuple
= calloc(n_ks_tuple
, sizeof (*ks_tuple
))) == NULL
) {
494 krb5_free_principal(contextp
->context
, princ
);
498 for (i
= 0; i
< n_ks_tuple
; i
++) {
499 ret
= krb5_ret_int32(sp
, &ks_tuple
[i
].ks_enctype
);
501 krb5_free_principal(contextp
->context
, princ
);
505 ret
= krb5_ret_int32(sp
, &ks_tuple
[i
].ks_salttype
);
507 krb5_free_principal(contextp
->context
, princ
);
513 ret
= kadm5_randkey_principal_3(kadm_handlep
, princ
, keepold
,
514 n_ks_tuple
, ks_tuple
, &new_keys
,
516 krb5_free_principal(contextp
->context
, princ
);
519 krb5_storage_free(sp
);
520 sp
= krb5_storage_emem();
521 krb5_store_int32(sp
, ret
);
524 krb5_store_int32(sp
, n_keys
);
525 for(i
= 0; i
< n_keys
; i
++){
526 krb5_store_keyblock(sp
, new_keys
[i
]);
527 krb5_free_keyblock_contents(contextp
->context
, &new_keys
[i
]);
533 case kadm_get_privs
:{
535 ret
= kadm5_get_privs(kadm_handlep
, &privs
);
536 krb5_storage_free(sp
);
537 sp
= krb5_storage_emem();
538 krb5_store_int32(sp
, ret
);
540 krb5_store_uint32(sp
, privs
);
543 case kadm_get_princs
:{
545 ret
= krb5_ret_int32(sp
, &tmp
);
549 ret
= krb5_ret_string(sp
, &expression
);
554 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
,
555 expression
? expression
: "*");
556 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_LIST
, NULL
);
561 ret
= kadm5_get_principals(kadm_handlep
, expression
, &princs
, &n_princs
);
563 krb5_storage_free(sp
);
564 sp
= krb5_storage_emem();
565 krb5_store_int32(sp
, ret
);
568 krb5_store_int32(sp
, n_princs
);
569 for(i
= 0; i
< n_princs
; i
++)
570 krb5_store_string(sp
, princs
[i
]);
571 kadm5_free_name_list(kadm_handlep
, princs
, &n_princs
);
576 krb5_warnx(contextp
->context
, "%s: UNKNOWN OP %d", client
, cmd
);
577 krb5_storage_free(sp
);
578 sp
= krb5_storage_emem();
579 krb5_store_int32(sp
, KADM5_FAILURE
);
582 krb5_storage_to_data(sp
, out
);
583 krb5_storage_free(sp
);
586 krb5_warn(contextp
->context
, ret
, "%s", op
);
587 krb5_storage_seek(sp
, 0, SEEK_SET
);
588 krb5_store_int32(sp
, ret
);
589 krb5_storage_to_data(sp
, out
);
590 krb5_storage_free(sp
);
594 struct iter_aliases_ctx
{
595 HDB_Ext_Aliases aliases
;
602 iter_aliases(kadm5_principal_ent_rec
*from
,
603 struct iter_aliases_ctx
*ctx
,
615 if (ctx
->done
== 0) {
616 if (ctx
->alias_idx
< ctx
->aliases
.aliases
.len
) {
617 *out
= &ctx
->aliases
.aliases
.val
[ctx
->alias_idx
++];
620 /* Out of aliases in this TL, step to next TL */
621 ctx
->tl
= ctx
->tl
->tl_data_next
;
622 } else if (ctx
->done
< 0) {
623 /* Setup iteration context */
624 memset(ctx
, 0, sizeof(*ctx
));
626 ctx
->aliases
.aliases
.val
= NULL
;
627 ctx
->aliases
.aliases
.len
= 0;
628 ctx
->tl
= from
->tl_data
;
631 free_HDB_Ext_Aliases(&ctx
->aliases
);
634 /* Find TL with aliases */
635 for (; ctx
->tl
!= NULL
; ctx
->tl
= ctx
->tl
->tl_data_next
) {
636 if (ctx
->tl
->tl_data_type
!= KRB5_TL_EXTENSION
)
639 ret
= decode_HDB_extension(ctx
->tl
->tl_data_contents
,
640 ctx
->tl
->tl_data_length
,
644 if (ext
.data
.element
== choice_HDB_extension_data_aliases
&&
645 ext
.data
.u
.aliases
.aliases
.len
> 0) {
646 ctx
->aliases
= ext
.data
.u
.aliases
;
649 free_HDB_extension(&ext
);
652 if (ctx
->tl
!= NULL
&& ctx
->aliases
.aliases
.len
> 0) {
653 *out
= &ctx
->aliases
.aliases
.val
[ctx
->alias_idx
++];
662 check_aliases(kadm5_server_context
*contextp
,
663 kadm5_principal_ent_rec
*add_princ
,
664 kadm5_principal_ent_rec
*del_princ
)
667 struct iter_aliases_ctx iter
;
668 struct iter_aliases_ctx iter_del
;
669 krb5_principal new_name
, old_name
;
673 * Yeah, this is O(N^2). Gathering and sorting all the aliases
674 * would be a bit of a pain; if we ever have principals with enough
675 * aliases for this to be a problem, we can fix it then.
677 for (iter
.done
= -1; iter
.done
!= 1;) {
679 ret
= iter_aliases(add_princ
, &iter
, &new_name
);
684 for (iter_del
.done
= -1; iter_del
.done
!= 1;) {
685 ret
= iter_aliases(del_princ
, &iter_del
, &old_name
);
688 if (iter_del
.done
== 1)
690 if (!krb5_principal_compare(contextp
->context
, new_name
, old_name
))
692 free_HDB_Ext_Aliases(&iter_del
.aliases
);
698 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_ADD
, new_name
);
700 free_HDB_Ext_Aliases(&iter
.aliases
);
709 v5_loop (krb5_context contextp
,
710 krb5_auth_context ac
,
711 krb5_boolean initial
,
719 doing_useful_work
= 0;
722 ret
= krb5_read_priv_message(contextp
, ac
, &fd
, &in
);
723 if(ret
== HEIM_ERR_EOF
)
726 krb5_err(contextp
, 1, ret
, "krb5_read_priv_message");
727 doing_useful_work
= 1;
728 kadmind_dispatch(kadm_handlep
, initial
, &in
, &out
);
730 ret
= krb5_write_priv_message(contextp
, ac
, &fd
, &out
);
732 krb5_err(contextp
, 1, ret
, "krb5_write_priv_message");
737 match_appl_version(const void *data
, const char *appl_version
)
740 if(sscanf(appl_version
, "KADM0.%u", &minor
) != 1)
743 *(unsigned*)(intptr_t)data
= minor
;
748 handle_v5(krb5_context contextp
,
757 krb5_boolean initial
;
758 krb5_auth_context ac
= NULL
;
760 unsigned kadm_version
;
761 kadm5_config_params realm_params
;
763 ret
= krb5_recvauth_match_version(contextp
, &ac
, &fd
,
764 match_appl_version
, &kadm_version
,
765 NULL
, KRB5_RECVAUTH_IGNORE_VERSION
,
768 krb5_err(contextp
, 1, ret
, "krb5_recvauth");
770 ret
= krb5_unparse_name (contextp
, ticket
->server
, &server_name
);
772 krb5_err (contextp
, 1, ret
, "krb5_unparse_name");
774 if (strncmp (server_name
, KADM5_ADMIN_SERVICE
,
775 strlen(KADM5_ADMIN_SERVICE
)) != 0)
776 krb5_errx (contextp
, 1, "ticket for strange principal (%s)",
781 memset(&realm_params
, 0, sizeof(realm_params
));
783 if(kadm_version
== 1) {
785 ret
= krb5_read_priv_message(contextp
, ac
, &fd
, ¶ms
);
787 krb5_err(contextp
, 1, ret
, "krb5_read_priv_message");
788 _kadm5_unmarshal_params(contextp
, ¶ms
, &realm_params
);
791 initial
= ticket
->ticket
.flags
.initial
;
792 ret
= krb5_unparse_name(contextp
, ticket
->client
, &client
);
794 krb5_err (contextp
, 1, ret
, "krb5_unparse_name");
795 krb5_free_ticket (contextp
, ticket
);
796 ret
= kadm5_s_init_with_password_ctx(contextp
,
804 krb5_err (contextp
, 1, ret
, "kadm5_init_with_password_ctx");
805 v5_loop (contextp
, ac
, initial
, kadm_handlep
, fd
);
809 kadmind_loop(krb5_context contextp
,
813 u_char buf
[sizeof(KRB5_SENDAUTH_VERSION
) + 4];
817 n
= krb5_net_read(contextp
, &sock
, buf
, 4);
821 krb5_err(contextp
, 1, errno
, "read");
822 _krb5_get_int(buf
, &len
, 4);
824 if (len
== sizeof(KRB5_SENDAUTH_VERSION
)) {
826 n
= krb5_net_read(contextp
, &sock
, buf
+ 4, len
);
828 krb5_err (contextp
, 1, errno
, "reading sendauth version");
830 krb5_errx (contextp
, 1, "EOF reading sendauth version");
832 if(memcmp(buf
+ 4, KRB5_SENDAUTH_VERSION
, len
) == 0) {
833 handle_v5(contextp
, keytab
, sock
);
840 handle_mit(contextp
, buf
, len
, sock
);