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
, kvno
, tmp
;
47 kadm5_server_context
*contextp
= kadm_handlep
;
48 char client
[128], name
[128], name2
[128];
50 krb5_principal princ
= NULL
, princ2
= NULL
;
51 kadm5_principal_ent_rec ent
, ent_prev
;
52 char *password
= NULL
, *expression
;
53 krb5_keyblock
*new_keys
;
54 krb5_key_salt_tuple
*ks_tuple
= NULL
;
64 krb5_unparse_name_fixed(contextp
->context
, contextp
->caller
,
65 client
, sizeof(client
));
67 sp
= krb5_storage_from_data(in
);
69 ret
= krb5_enomem(contextp
->context
);
73 krb5_ret_int32(sp
, &cmd
);
77 ret
= krb5_ret_principal(sp
, &princ
);
80 ret
= krb5_ret_int32(sp
, &mask
);
84 mask
|= KADM5_PRINCIPAL
;
85 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
86 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
88 /* If the caller doesn't have KADM5_PRIV_GET, we're done. */
89 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_GET
, 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.
123 ret
= kadm5_get_principal(kadm_handlep
, princ
, &ent
, mask
);
124 krb5_storage_free(sp
);
125 sp
= krb5_storage_emem();
127 ret
= krb5_enomem(contextp
->context
);
130 krb5_store_int32(sp
, ret
);
133 kadm5_store_principal_ent(sp
, &ent
);
135 kadm5_store_principal_ent_nokeys(sp
, &ent
);
136 kadm5_free_principal_ent(kadm_handlep
, &ent
);
142 ret
= krb5_ret_principal(sp
, &princ
);
145 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
146 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
147 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_DELETE
, 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_storage_free(sp
);
158 sp
= krb5_storage_emem();
160 ret
= krb5_enomem(contextp
->context
);
163 krb5_store_int32(sp
, ret
);
168 ret
= kadm5_ret_principal_ent(sp
, &ent
);
171 ret
= krb5_ret_int32(sp
, &mask
);
173 kadm5_free_principal_ent(kadm_handlep
, &ent
);
176 ret
= krb5_ret_string(sp
, &password
);
178 kadm5_free_principal_ent(kadm_handlep
, &ent
);
181 krb5_unparse_name_fixed(contextp
->context
, ent
.principal
,
183 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
184 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_ADD
,
187 kadm5_free_principal_ent(kadm_handlep
, &ent
);
190 if ((mask
& KADM5_TL_DATA
)) {
192 * Also check that the caller can create the aliases, if the
193 * new principal has any.
195 ret
= check_aliases(contextp
, &ent
, NULL
);
197 kadm5_free_principal_ent(kadm_handlep
, &ent
);
201 ret
= kadm5_create_principal(kadm_handlep
, &ent
,
203 kadm5_free_principal_ent(kadm_handlep
, &ent
);
204 krb5_storage_free(sp
);
205 sp
= krb5_storage_emem();
207 ret
= krb5_enomem(contextp
->context
);
210 krb5_store_int32(sp
, ret
);
215 ret
= kadm5_ret_principal_ent(sp
, &ent
);
218 ret
= krb5_ret_int32(sp
, &mask
);
220 kadm5_free_principal_ent(contextp
, &ent
);
223 krb5_unparse_name_fixed(contextp
->context
, ent
.principal
,
225 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
226 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_MODIFY
,
229 kadm5_free_principal_ent(contextp
, &ent
);
232 if ((mask
& KADM5_TL_DATA
)) {
234 * Also check that the caller can create aliases that are in
235 * the new entry but not the old one. There's no need to
236 * check that the caller can delete aliases it wants to
237 * drop. See also handling of rename.
239 ret
= kadm5_get_principal(kadm_handlep
, ent
.principal
, &ent_prev
, mask
);
241 kadm5_free_principal_ent(contextp
, &ent
);
244 ret
= check_aliases(contextp
, &ent
, &ent_prev
);
245 kadm5_free_principal_ent(contextp
, &ent_prev
);
247 kadm5_free_principal_ent(contextp
, &ent
);
251 ret
= kadm5_modify_principal(kadm_handlep
, &ent
, mask
);
252 kadm5_free_principal_ent(kadm_handlep
, &ent
);
253 krb5_storage_free(sp
);
254 sp
= krb5_storage_emem();
256 ret
= krb5_enomem(contextp
->context
);
259 krb5_store_int32(sp
, ret
);
264 ret
= krb5_ret_principal(sp
, &princ
);
267 ret
= krb5_ret_int32(sp
, &kvno
);
268 if (ret
== HEIM_ERR_EOF
) {
273 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
274 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
275 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_CPW
, princ
);
279 ret
= kadm5_prune_principal(kadm_handlep
, princ
, kvno
);
280 krb5_storage_free(sp
);
281 sp
= krb5_storage_emem();
283 ret
= krb5_enomem(contextp
->context
);
286 krb5_store_int32(sp
, ret
);
291 ret
= krb5_ret_principal(sp
, &princ
);
294 ret
= krb5_ret_principal(sp
, &princ2
);
298 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
299 krb5_unparse_name_fixed(contextp
->context
, princ2
,
300 name2
, sizeof(name2
));
301 krb5_warnx(contextp
->context
, "%s: %s %s -> %s",
302 client
, op
, name
, name2
);
303 ret
= _kadm5_acl_check_permission(contextp
,
308 * Also require modify for the principal. For backwards
309 * compatibility, allow delete permission on the old name to
310 * cure lack of modify permission on the old name.
312 ret
= _kadm5_acl_check_permission(contextp
,
316 ret
= _kadm5_acl_check_permission(contextp
,
324 ret
= kadm5_rename_principal(kadm_handlep
, princ
, princ2
);
325 krb5_storage_free(sp
);
326 sp
= krb5_storage_emem();
328 ret
= krb5_enomem(contextp
->context
);
331 krb5_store_int32(sp
, ret
);
335 krb5_boolean is_self_cpw
, allow_self_cpw
;
338 ret
= krb5_ret_principal(sp
, &princ
);
341 ret
= krb5_ret_string(sp
, &password
);
345 ret
= krb5_ret_int32(sp
, &keepold
);
346 if (ret
&& ret
!= HEIM_ERR_EOF
)
349 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
350 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
353 * Change password requests are subject to ACLs unless the principal is
354 * changing their own password and the initial ticket flag is set, and
355 * the allow_self_change_password configuration option is TRUE.
358 krb5_principal_compare(contextp
->context
, contextp
->caller
, princ
);
360 krb5_config_get_bool_default(contextp
->context
, NULL
, TRUE
,
361 "kadmin", "allow_self_change_password", NULL
);
362 if (!(is_self_cpw
&& initial
&& allow_self_cpw
)) {
363 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_CPW
, princ
);
368 ret
= kadm5_chpass_principal_3(kadm_handlep
, princ
, keepold
, 0, NULL
,
370 krb5_storage_free(sp
);
371 sp
= krb5_storage_emem();
373 ret
= krb5_enomem(contextp
->context
);
376 krb5_store_int32(sp
, ret
);
379 case kadm_chpass_with_key
:{
381 krb5_key_data
*key_data
;
384 op
= "CHPASS_WITH_KEY";
385 ret
= krb5_ret_principal(sp
, &princ
);
388 ret
= krb5_ret_int32(sp
, &n_key_data
);
392 ret
= krb5_ret_int32(sp
, &keepold
);
393 if (ret
&& ret
!= HEIM_ERR_EOF
)
396 /* n_key_data will be squeezed into an int16_t below. */
397 if (n_key_data
< 0 || n_key_data
>= 1 << 16 ||
398 (size_t)n_key_data
> UINT_MAX
/sizeof(*key_data
)) {
403 key_data
= malloc (n_key_data
* sizeof(*key_data
));
404 if (key_data
== NULL
&& n_key_data
!= 0) {
405 ret
= krb5_enomem(contextp
->context
);
409 for (i
= 0; i
< n_key_data
; ++i
) {
410 ret
= kadm5_ret_key_data (sp
, &key_data
[i
]);
414 kadm5_free_key_data (contextp
, &dummy
, key_data
);
420 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
421 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
424 * The change is only allowed if the user is on the CPW ACL,
425 * this it to force password quality check on the user.
428 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_CPW
, princ
);
430 int16_t dummy
= n_key_data
;
432 kadm5_free_key_data (contextp
, &dummy
, key_data
);
436 ret
= kadm5_chpass_principal_with_key_3(kadm_handlep
, princ
, keepold
,
437 n_key_data
, key_data
);
439 int16_t dummy
= n_key_data
;
440 kadm5_free_key_data (contextp
, &dummy
, key_data
);
443 krb5_storage_free(sp
);
444 sp
= krb5_storage_emem();
446 ret
= krb5_enomem(contextp
->context
);
449 krb5_store_int32(sp
, ret
);
456 ret
= krb5_ret_principal(sp
, &princ
);
459 krb5_unparse_name_fixed(contextp
->context
, princ
, name
, sizeof(name
));
460 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
, name
);
462 * The change is allowed if at least one of:
463 * a) it's for the principal him/herself and this was an initial ticket
464 * b) the user is on the CPW ACL.
468 && krb5_principal_compare (contextp
->context
, contextp
->caller
,
472 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_CPW
, princ
);
478 * See comments in kadm5_c_randkey_principal() regarding the
481 ret
= krb5_ret_int32(sp
, &keepold
);
482 if (ret
!= 0 && ret
!= HEIM_ERR_EOF
)
485 ret
= krb5_ret_int32(sp
, &n_ks_tuple
);
486 if (ret
== HEIM_ERR_EOF
) {
487 const char *enctypes
;
490 enctypes
= krb5_config_get_string(contextp
->context
, NULL
,
492 krb5_principal_get_realm(contextp
->context
,
494 "supported_enctypes", NULL
);
495 if (enctypes
== NULL
|| enctypes
[0] == '\0')
496 enctypes
= "aes128-cts-hmac-sha1-96";
497 ret
= krb5_string_to_keysalts2(contextp
->context
, enctypes
,
504 if (n_ks_tuple
< 0) {
509 if ((ks_tuple
= calloc(n_ks_tuple
, sizeof (*ks_tuple
))) == NULL
) {
514 for (i
= 0; i
< n_ks_tuple
; i
++) {
515 ret
= krb5_ret_int32(sp
, &ks_tuple
[i
].ks_enctype
);
520 ret
= krb5_ret_int32(sp
, &ks_tuple
[i
].ks_salttype
);
526 ret
= kadm5_randkey_principal_3(kadm_handlep
, princ
, keepold
,
527 n_ks_tuple
, ks_tuple
, &new_keys
,
531 krb5_storage_free(sp
);
532 sp
= krb5_storage_emem();
534 ret
= krb5_enomem(contextp
->context
);
537 krb5_store_int32(sp
, ret
);
539 krb5_store_int32(sp
, n_keys
);
540 for (i
= 0; i
< n_keys
; i
++){
542 ret
= krb5_store_keyblock(sp
, new_keys
[i
]);
543 krb5_free_keyblock_contents(contextp
->context
, &new_keys
[i
]);
549 case kadm_get_privs
:{
551 ret
= kadm5_get_privs(kadm_handlep
, &privs
);
552 krb5_storage_free(sp
);
553 sp
= krb5_storage_emem();
555 ret
= krb5_enomem(contextp
->context
);
558 krb5_store_int32(sp
, ret
);
560 krb5_store_uint32(sp
, privs
);
563 case kadm_get_princs
:{
565 ret
= krb5_ret_int32(sp
, &tmp
);
569 ret
= krb5_ret_string(sp
, &expression
);
574 krb5_warnx(contextp
->context
, "%s: %s %s", client
, op
,
575 expression
? expression
: "*");
576 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_LIST
, NULL
);
581 ret
= kadm5_get_principals(kadm_handlep
, expression
, &princs
, &n_princs
);
583 krb5_storage_free(sp
);
584 sp
= krb5_storage_emem();
586 ret
= krb5_enomem(contextp
->context
);
589 krb5_store_int32(sp
, ret
);
592 krb5_store_int32(sp
, n_princs
);
593 for(i
= 0; i
< n_princs
; i
++)
594 krb5_store_string(sp
, princs
[i
]);
595 kadm5_free_name_list(kadm_handlep
, princs
, &n_princs
);
600 krb5_warnx(contextp
->context
, "%s: UNKNOWN OP %d", client
, cmd
);
601 krb5_storage_free(sp
);
602 sp
= krb5_storage_emem();
604 ret
= krb5_enomem(contextp
->context
);
607 krb5_store_int32(sp
, KADM5_FAILURE
);
610 if (password
!= NULL
) {
611 len
= strlen(password
);
612 memset_s(password
, len
, 0, len
);
615 krb5_storage_to_data(sp
, out
);
616 krb5_storage_free(sp
);
618 krb5_free_principal(contextp
->context
, princ
);
620 krb5_free_principal(contextp
->context
, princ2
);
623 if (password
!= NULL
) {
624 len
= strlen(password
);
625 memset_s(password
, len
, 0, len
);
628 krb5_warn(contextp
->context
, ret
, "%s", op
);
630 krb5_storage_seek(sp
, 0, SEEK_SET
);
631 krb5_store_int32(sp
, ret
);
632 krb5_storage_to_data(sp
, out
);
633 krb5_storage_free(sp
);
636 krb5_free_principal(contextp
->context
, princ
);
638 krb5_free_principal(contextp
->context
, princ2
);
642 struct iter_aliases_ctx
{
643 HDB_Ext_Aliases aliases
;
650 iter_aliases(kadm5_principal_ent_rec
*from
,
651 struct iter_aliases_ctx
*ctx
,
663 if (ctx
->done
== 0) {
664 if (ctx
->alias_idx
< ctx
->aliases
.aliases
.len
) {
665 *out
= &ctx
->aliases
.aliases
.val
[ctx
->alias_idx
++];
668 /* Out of aliases in this TL, step to next TL */
669 ctx
->tl
= ctx
->tl
->tl_data_next
;
670 } else if (ctx
->done
< 0) {
671 /* Setup iteration context */
672 memset(ctx
, 0, sizeof(*ctx
));
674 ctx
->aliases
.aliases
.val
= NULL
;
675 ctx
->aliases
.aliases
.len
= 0;
676 ctx
->tl
= from
->tl_data
;
679 free_HDB_Ext_Aliases(&ctx
->aliases
);
682 /* Find TL with aliases */
683 for (; ctx
->tl
!= NULL
; ctx
->tl
= ctx
->tl
->tl_data_next
) {
684 if (ctx
->tl
->tl_data_type
!= KRB5_TL_EXTENSION
)
687 ret
= decode_HDB_extension(ctx
->tl
->tl_data_contents
,
688 ctx
->tl
->tl_data_length
,
692 if (ext
.data
.element
== choice_HDB_extension_data_aliases
&&
693 ext
.data
.u
.aliases
.aliases
.len
> 0) {
694 ctx
->aliases
= ext
.data
.u
.aliases
;
697 free_HDB_extension(&ext
);
700 if (ctx
->tl
!= NULL
&& ctx
->aliases
.aliases
.len
> 0) {
701 *out
= &ctx
->aliases
.aliases
.val
[ctx
->alias_idx
++];
710 check_aliases(kadm5_server_context
*contextp
,
711 kadm5_principal_ent_rec
*add_princ
,
712 kadm5_principal_ent_rec
*del_princ
)
715 struct iter_aliases_ctx iter
;
716 struct iter_aliases_ctx iter_del
;
717 krb5_principal new_name
, old_name
;
721 * Yeah, this is O(N^2). Gathering and sorting all the aliases
722 * would be a bit of a pain; if we ever have principals with enough
723 * aliases for this to be a problem, we can fix it then.
725 for (iter
.done
= -1; iter
.done
!= 1;) {
727 ret
= iter_aliases(add_princ
, &iter
, &new_name
);
732 for (iter_del
.done
= -1; iter_del
.done
!= 1;) {
733 ret
= iter_aliases(del_princ
, &iter_del
, &old_name
);
736 if (iter_del
.done
== 1)
738 if (!krb5_principal_compare(contextp
->context
, new_name
, old_name
))
740 free_HDB_Ext_Aliases(&iter_del
.aliases
);
746 ret
= _kadm5_acl_check_permission(contextp
, KADM5_PRIV_ADD
, new_name
);
748 free_HDB_Ext_Aliases(&iter
.aliases
);
757 v5_loop (krb5_context contextp
,
758 krb5_auth_context ac
,
759 krb5_boolean initial
,
767 doing_useful_work
= 0;
770 ret
= krb5_read_priv_message(contextp
, ac
, &fd
, &in
);
771 if(ret
== HEIM_ERR_EOF
)
774 krb5_err(contextp
, 1, ret
, "krb5_read_priv_message");
775 doing_useful_work
= 1;
776 ret
= kadmind_dispatch(kadm_handlep
, initial
, &in
, &out
);
778 krb5_err(contextp
, 1, ret
, "kadmind_dispatch");
780 ret
= krb5_write_priv_message(contextp
, ac
, &fd
, &out
);
781 krb5_data_free(&out
);
783 krb5_err(contextp
, 1, ret
, "krb5_write_priv_message");
788 match_appl_version(const void *data
, const char *appl_version
)
791 if(sscanf(appl_version
, "KADM0.%u", &minor
) != 1)
794 *(unsigned*)(intptr_t)data
= minor
;
799 handle_v5(krb5_context contextp
,
808 krb5_boolean initial
;
809 krb5_auth_context ac
= NULL
;
811 unsigned kadm_version
= 1;
812 kadm5_config_params realm_params
;
814 ret
= krb5_recvauth_match_version(contextp
, &ac
, &fd
,
815 match_appl_version
, &kadm_version
,
816 NULL
, KRB5_RECVAUTH_IGNORE_VERSION
,
819 krb5_err(contextp
, 1, ret
, "krb5_recvauth");
821 ret
= krb5_unparse_name (contextp
, ticket
->server
, &server_name
);
823 krb5_err (contextp
, 1, ret
, "krb5_unparse_name");
825 if (strncmp (server_name
, KADM5_ADMIN_SERVICE
,
826 strlen(KADM5_ADMIN_SERVICE
)) != 0)
827 krb5_errx (contextp
, 1, "ticket for strange principal (%s)",
832 memset(&realm_params
, 0, sizeof(realm_params
));
834 if(kadm_version
== 1) {
836 ret
= krb5_read_priv_message(contextp
, ac
, &fd
, ¶ms
);
838 krb5_err(contextp
, 1, ret
, "krb5_read_priv_message");
839 _kadm5_unmarshal_params(contextp
, ¶ms
, &realm_params
);
842 initial
= ticket
->ticket
.flags
.initial
;
843 ret
= krb5_unparse_name(contextp
, ticket
->client
, &client
);
845 krb5_err (contextp
, 1, ret
, "krb5_unparse_name");
846 krb5_free_ticket (contextp
, ticket
);
847 ret
= kadm5_s_init_with_password_ctx(contextp
,
855 krb5_err (contextp
, 1, ret
, "kadm5_init_with_password_ctx");
856 v5_loop (contextp
, ac
, initial
, kadm_handlep
, fd
);
860 kadmind_loop(krb5_context contextp
,
864 u_char buf
[sizeof(KRB5_SENDAUTH_VERSION
) + 4];
868 n
= krb5_net_read(contextp
, &sock
, buf
, 4);
872 krb5_err(contextp
, 1, errno
, "read");
873 _krb5_get_int(buf
, &len
, 4);
875 if (len
== sizeof(KRB5_SENDAUTH_VERSION
)) {
877 n
= krb5_net_read(contextp
, &sock
, buf
+ 4, len
);
879 krb5_err (contextp
, 1, errno
, "reading sendauth version");
881 krb5_errx (contextp
, 1, "EOF reading sendauth version");
883 if(memcmp(buf
+ 4, KRB5_SENDAUTH_VERSION
, len
) == 0) {
884 handle_v5(contextp
, keytab
, sock
);
891 handle_mit(contextp
, buf
, len
, sock
);