155682Smarkm/*
2178825Sdfr * Copyright (c) 1997 - 2005 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kadmin_locl.h"
3555682Smarkm#include <krb5-private.h>
3655682Smarkm
37178825SdfrRCSID("$Id: server.c 17611 2006-06-02 22:10:21Z lha $");
3855682Smarkm
3955682Smarkmstatic kadm5_ret_t
4055682Smarkmkadmind_dispatch(void *kadm_handle, krb5_boolean initial,
4155682Smarkm		 krb5_data *in, krb5_data *out)
4255682Smarkm{
4355682Smarkm    kadm5_ret_t ret;
4455682Smarkm    int32_t cmd, mask, tmp;
4555682Smarkm    kadm5_server_context *context = kadm_handle;
4655682Smarkm    char client[128], name[128], name2[128];
4755682Smarkm    char *op = "";
4855682Smarkm    krb5_principal princ, princ2;
4955682Smarkm    kadm5_principal_ent_rec ent;
50178825Sdfr    char *password, *expression;
5155682Smarkm    krb5_keyblock *new_keys;
5255682Smarkm    int n_keys;
5355682Smarkm    char **princs;
5455682Smarkm    int n_princs;
5555682Smarkm    krb5_storage *sp;
5655682Smarkm
5755682Smarkm    krb5_unparse_name_fixed(context->context, context->caller,
5855682Smarkm			    client, sizeof(client));
5955682Smarkm
6055682Smarkm    sp = krb5_storage_from_data(in);
6155682Smarkm
6255682Smarkm    krb5_ret_int32(sp, &cmd);
6355682Smarkm    switch(cmd){
6455682Smarkm    case kadm_get:{
6555682Smarkm	op = "GET";
6655682Smarkm	ret = krb5_ret_principal(sp, &princ);
6755682Smarkm	if(ret)
6855682Smarkm	    goto fail;
6955682Smarkm	ret = krb5_ret_int32(sp, &mask);
7055682Smarkm	if(ret){
7155682Smarkm	    krb5_free_principal(context->context, princ);
7255682Smarkm	    goto fail;
7355682Smarkm	}
7455682Smarkm	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
7555682Smarkm	krb5_warnx(context->context, "%s: %s %s", client, op, name);
7672445Sassar	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_GET, princ);
7755682Smarkm	if(ret){
7855682Smarkm	    krb5_free_principal(context->context, princ);
7955682Smarkm	    goto fail;
8055682Smarkm	}
8155682Smarkm	ret = kadm5_get_principal(kadm_handle, princ, &ent, mask);
8255682Smarkm	krb5_storage_free(sp);
8355682Smarkm	sp = krb5_storage_emem();
8455682Smarkm	krb5_store_int32(sp, ret);
8555682Smarkm	if(ret == 0){
8655682Smarkm	    kadm5_store_principal_ent(sp, &ent);
8755682Smarkm	    kadm5_free_principal_ent(kadm_handle, &ent);
8855682Smarkm	}
8955682Smarkm	krb5_free_principal(context->context, princ);
9055682Smarkm	break;
9155682Smarkm    }
9255682Smarkm    case kadm_delete:{
9355682Smarkm	op = "DELETE";
9455682Smarkm	ret = krb5_ret_principal(sp, &princ);
9555682Smarkm	if(ret)
9655682Smarkm	    goto fail;
9755682Smarkm	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
9855682Smarkm	krb5_warnx(context->context, "%s: %s %s", client, op, name);
9972445Sassar	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_DELETE, princ);
10055682Smarkm	if(ret){
10155682Smarkm	    krb5_free_principal(context->context, princ);
10255682Smarkm	    goto fail;
10355682Smarkm	}
10455682Smarkm	ret = kadm5_delete_principal(kadm_handle, princ);
10555682Smarkm	krb5_free_principal(context->context, princ);
10655682Smarkm	krb5_storage_free(sp);
10755682Smarkm	sp = krb5_storage_emem();
10855682Smarkm	krb5_store_int32(sp, ret);
10955682Smarkm	break;
11055682Smarkm    }
11155682Smarkm    case kadm_create:{
11255682Smarkm	op = "CREATE";
11355682Smarkm	ret = kadm5_ret_principal_ent(sp, &ent);
11455682Smarkm	if(ret)
11555682Smarkm	    goto fail;
11655682Smarkm	ret = krb5_ret_int32(sp, &mask);
11755682Smarkm	if(ret){
11855682Smarkm	    kadm5_free_principal_ent(context->context, &ent);
11955682Smarkm	    goto fail;
12055682Smarkm	}
12155682Smarkm	ret = krb5_ret_string(sp, &password);
12255682Smarkm	if(ret){
12355682Smarkm	    kadm5_free_principal_ent(context->context, &ent);
12455682Smarkm	    goto fail;
12555682Smarkm	}
12655682Smarkm	krb5_unparse_name_fixed(context->context, ent.principal,
12755682Smarkm				name, sizeof(name));
12855682Smarkm	krb5_warnx(context->context, "%s: %s %s", client, op, name);
12972445Sassar	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_ADD,
13072445Sassar					  ent.principal);
13155682Smarkm	if(ret){
13255682Smarkm	    kadm5_free_principal_ent(context->context, &ent);
13355682Smarkm	    memset(password, 0, strlen(password));
13455682Smarkm	    free(password);
13555682Smarkm	    goto fail;
13655682Smarkm	}
13755682Smarkm	ret = kadm5_create_principal(kadm_handle, &ent,
13855682Smarkm				     mask, password);
13955682Smarkm	kadm5_free_principal_ent(kadm_handle, &ent);
14055682Smarkm	memset(password, 0, strlen(password));
14155682Smarkm	free(password);
14255682Smarkm	krb5_storage_free(sp);
14355682Smarkm	sp = krb5_storage_emem();
14455682Smarkm	krb5_store_int32(sp, ret);
14555682Smarkm	break;
14655682Smarkm    }
14755682Smarkm    case kadm_modify:{
14855682Smarkm	op = "MODIFY";
14955682Smarkm	ret = kadm5_ret_principal_ent(sp, &ent);
15055682Smarkm	if(ret)
15155682Smarkm	    goto fail;
15255682Smarkm	ret = krb5_ret_int32(sp, &mask);
15355682Smarkm	if(ret){
15455682Smarkm	    kadm5_free_principal_ent(context, &ent);
15555682Smarkm	    goto fail;
15655682Smarkm	}
15755682Smarkm	krb5_unparse_name_fixed(context->context, ent.principal,
15855682Smarkm				name, sizeof(name));
15955682Smarkm	krb5_warnx(context->context, "%s: %s %s", client, op, name);
16072445Sassar	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_MODIFY,
16172445Sassar					  ent.principal);
16255682Smarkm	if(ret){
16355682Smarkm	    kadm5_free_principal_ent(context, &ent);
16455682Smarkm	    goto fail;
16555682Smarkm	}
16655682Smarkm	ret = kadm5_modify_principal(kadm_handle, &ent, mask);
16755682Smarkm	kadm5_free_principal_ent(kadm_handle, &ent);
16855682Smarkm	krb5_storage_free(sp);
16955682Smarkm	sp = krb5_storage_emem();
17055682Smarkm	krb5_store_int32(sp, ret);
17155682Smarkm	break;
17255682Smarkm    }
17355682Smarkm    case kadm_rename:{
17455682Smarkm	op = "RENAME";
17555682Smarkm	ret = krb5_ret_principal(sp, &princ);
17655682Smarkm	if(ret)
17755682Smarkm	    goto fail;
17855682Smarkm	ret = krb5_ret_principal(sp, &princ2);
17955682Smarkm	if(ret){
18055682Smarkm	    krb5_free_principal(context->context, princ);
18155682Smarkm	    goto fail;
18255682Smarkm	}
18355682Smarkm	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
18455682Smarkm	krb5_unparse_name_fixed(context->context, princ2, name2, sizeof(name2));
18555682Smarkm	krb5_warnx(context->context, "%s: %s %s -> %s",
18655682Smarkm		   client, op, name, name2);
18755682Smarkm	ret = _kadm5_acl_check_permission(context,
18872445Sassar					  KADM5_PRIV_ADD,
18972445Sassar					  princ2)
19072445Sassar	    || _kadm5_acl_check_permission(context,
19172445Sassar					   KADM5_PRIV_DELETE,
19272445Sassar					   princ);
19355682Smarkm	if(ret){
19455682Smarkm	    krb5_free_principal(context->context, princ);
195178825Sdfr	    krb5_free_principal(context->context, princ2);
19655682Smarkm	    goto fail;
19755682Smarkm	}
19855682Smarkm	ret = kadm5_rename_principal(kadm_handle, princ, princ2);
19955682Smarkm	krb5_free_principal(context->context, princ);
20055682Smarkm	krb5_free_principal(context->context, princ2);
20155682Smarkm	krb5_storage_free(sp);
20255682Smarkm	sp = krb5_storage_emem();
20355682Smarkm	krb5_store_int32(sp, ret);
20455682Smarkm	break;
20555682Smarkm    }
20655682Smarkm    case kadm_chpass:{
20755682Smarkm	op = "CHPASS";
20855682Smarkm	ret = krb5_ret_principal(sp, &princ);
20955682Smarkm	if(ret)
21055682Smarkm	    goto fail;
21155682Smarkm	ret = krb5_ret_string(sp, &password);
21255682Smarkm	if(ret){
21355682Smarkm	    krb5_free_principal(context->context, princ);
21455682Smarkm	    goto fail;
21555682Smarkm	}
21655682Smarkm	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
21755682Smarkm	krb5_warnx(context->context, "%s: %s %s", client, op, name);
21855682Smarkm
21955682Smarkm	/*
22055682Smarkm	 * The change is allowed if at least one of:
221120945Snectar
222120945Snectar	 * a) it's for the principal him/herself and this was an
223120945Snectar	 *    initial ticket, but then, check with the password quality
224120945Snectar	 *    function.
22555682Smarkm	 * b) the user is on the CPW ACL.
22655682Smarkm	 */
22755682Smarkm
22855682Smarkm	if (initial
22955682Smarkm	    && krb5_principal_compare (context->context, context->caller,
23055682Smarkm				       princ))
231120945Snectar	{
232120945Snectar	    krb5_data pwd_data;
233120945Snectar	    const char *pwd_reason;
234120945Snectar
235120945Snectar	    pwd_data.data = password;
236120945Snectar	    pwd_data.length = strlen(password);
237120945Snectar
238120945Snectar	    pwd_reason = kadm5_check_password_quality (context->context,
239120945Snectar						       princ, &pwd_data);
240120945Snectar	    if (pwd_reason != NULL)
241120945Snectar		ret = KADM5_PASS_Q_DICT;
242120945Snectar	    else
243120945Snectar		ret = 0;
244120945Snectar	} else
24572445Sassar	    ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ);
24655682Smarkm
24755682Smarkm	if(ret) {
24855682Smarkm	    krb5_free_principal(context->context, princ);
249120945Snectar	    memset(password, 0, strlen(password));
250120945Snectar	    free(password);
25155682Smarkm	    goto fail;
25255682Smarkm	}
25355682Smarkm	ret = kadm5_chpass_principal(kadm_handle, princ, password);
25455682Smarkm	krb5_free_principal(context->context, princ);
25555682Smarkm	memset(password, 0, strlen(password));
25655682Smarkm	free(password);
25755682Smarkm	krb5_storage_free(sp);
25855682Smarkm	sp = krb5_storage_emem();
25955682Smarkm	krb5_store_int32(sp, ret);
26055682Smarkm	break;
26155682Smarkm    }
26272445Sassar    case kadm_chpass_with_key:{
26372445Sassar	int i;
26472445Sassar	krb5_key_data *key_data;
26572445Sassar	int n_key_data;
26672445Sassar
26772445Sassar	op = "CHPASS_WITH_KEY";
26872445Sassar	ret = krb5_ret_principal(sp, &princ);
26972445Sassar	if(ret)
27072445Sassar	    goto fail;
27172445Sassar	ret = krb5_ret_int32(sp, &n_key_data);
27272445Sassar	if (ret) {
27372445Sassar	    krb5_free_principal(context->context, princ);
27472445Sassar	    goto fail;
27572445Sassar	}
276103423Snectar	/* n_key_data will be squeezed into an int16_t below. */
277103423Snectar	if (n_key_data < 0 || n_key_data >= 1 << 16 ||
278103423Snectar	    n_key_data > UINT_MAX/sizeof(*key_data)) {
279103423Snectar	    ret = ERANGE;
280103423Snectar	    krb5_free_principal(context->context, princ);
281103423Snectar	    goto fail;
282103423Snectar	}
28372445Sassar
28472445Sassar	key_data = malloc (n_key_data * sizeof(*key_data));
28572445Sassar	if (key_data == NULL) {
28672445Sassar	    ret = ENOMEM;
28772445Sassar	    krb5_free_principal(context->context, princ);
28872445Sassar	    goto fail;
28972445Sassar	}
29072445Sassar
29172445Sassar	for (i = 0; i < n_key_data; ++i) {
29272445Sassar	    ret = kadm5_ret_key_data (sp, &key_data[i]);
29372445Sassar	    if (ret) {
29472445Sassar		int16_t dummy = i;
29572445Sassar
29672445Sassar		kadm5_free_key_data (context, &dummy, key_data);
29772445Sassar		free (key_data);
29872445Sassar		krb5_free_principal(context->context, princ);
29972445Sassar		goto fail;
30072445Sassar	    }
30172445Sassar	}
30272445Sassar
30372445Sassar	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
30472445Sassar	krb5_warnx(context->context, "%s: %s %s", client, op, name);
30572445Sassar
30672445Sassar	/*
307120945Snectar	 * The change is only allowed if the user is on the CPW ACL,
308120945Snectar	 * this it to force password quality check on the user.
30972445Sassar	 */
31072445Sassar
311120945Snectar	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ);
31272445Sassar	if(ret) {
31372445Sassar	    int16_t dummy = n_key_data;
31472445Sassar
31572445Sassar	    kadm5_free_key_data (context, &dummy, key_data);
31672445Sassar	    free (key_data);
31772445Sassar	    krb5_free_principal(context->context, princ);
31872445Sassar	    goto fail;
31972445Sassar	}
32072445Sassar	ret = kadm5_chpass_principal_with_key(kadm_handle, princ,
32172445Sassar					      n_key_data, key_data);
32272445Sassar	{
32372445Sassar	    int16_t dummy = n_key_data;
32472445Sassar	    kadm5_free_key_data (context, &dummy, key_data);
32572445Sassar	}
32672445Sassar	free (key_data);
32772445Sassar	krb5_free_principal(context->context, princ);
32872445Sassar	krb5_storage_free(sp);
32972445Sassar	sp = krb5_storage_emem();
33072445Sassar	krb5_store_int32(sp, ret);
33172445Sassar	break;
33272445Sassar    }
33355682Smarkm    case kadm_randkey:{
33455682Smarkm	op = "RANDKEY";
33555682Smarkm	ret = krb5_ret_principal(sp, &princ);
33655682Smarkm	if(ret)
33755682Smarkm	    goto fail;
33855682Smarkm	krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
33955682Smarkm	krb5_warnx(context->context, "%s: %s %s", client, op, name);
34055682Smarkm	/*
34155682Smarkm	 * The change is allowed if at least one of:
34255682Smarkm	 * a) it's for the principal him/herself and this was an initial ticket
34355682Smarkm	 * b) the user is on the CPW ACL.
34455682Smarkm	 */
34555682Smarkm
34655682Smarkm	if (initial
34755682Smarkm	    && krb5_principal_compare (context->context, context->caller,
34855682Smarkm				       princ))
34955682Smarkm	    ret = 0;
35055682Smarkm	else
35172445Sassar	    ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ);
35255682Smarkm
35355682Smarkm	if(ret) {
35455682Smarkm	    krb5_free_principal(context->context, princ);
35555682Smarkm	    goto fail;
35655682Smarkm	}
35755682Smarkm	ret = kadm5_randkey_principal(kadm_handle, princ,
35855682Smarkm				      &new_keys, &n_keys);
35955682Smarkm	krb5_free_principal(context->context, princ);
36055682Smarkm	krb5_storage_free(sp);
36155682Smarkm	sp = krb5_storage_emem();
36255682Smarkm	krb5_store_int32(sp, ret);
36355682Smarkm	if(ret == 0){
36455682Smarkm	    int i;
36555682Smarkm	    krb5_store_int32(sp, n_keys);
36655682Smarkm	    for(i = 0; i < n_keys; i++){
36755682Smarkm		krb5_store_keyblock(sp, new_keys[i]);
36855682Smarkm		krb5_free_keyblock_contents(context->context, &new_keys[i]);
36955682Smarkm	    }
37055682Smarkm	}
37155682Smarkm	break;
37255682Smarkm    }
37355682Smarkm    case kadm_get_privs:{
374178825Sdfr	uint32_t privs;
375178825Sdfr	ret = kadm5_get_privs(kadm_handle, &privs);
37655682Smarkm	krb5_storage_free(sp);
37755682Smarkm	sp = krb5_storage_emem();
37855682Smarkm	krb5_store_int32(sp, ret);
37955682Smarkm	if(ret == 0)
380178825Sdfr	    krb5_store_uint32(sp, privs);
38155682Smarkm	break;
38255682Smarkm    }
38355682Smarkm    case kadm_get_princs:{
38455682Smarkm	op = "LIST";
38555682Smarkm	ret = krb5_ret_int32(sp, &tmp);
38655682Smarkm	if(ret)
38755682Smarkm	    goto fail;
38855682Smarkm	if(tmp){
389178825Sdfr	    ret = krb5_ret_string(sp, &expression);
39055682Smarkm	    if(ret)
39155682Smarkm		goto fail;
39255682Smarkm	}else
393178825Sdfr	    expression = NULL;
394178825Sdfr	krb5_warnx(context->context, "%s: %s %s", client, op,
395178825Sdfr		   expression ? expression : "*");
39672445Sassar	ret = _kadm5_acl_check_permission(context, KADM5_PRIV_LIST, NULL);
39755682Smarkm	if(ret){
398178825Sdfr	    free(expression);
39955682Smarkm	    goto fail;
40055682Smarkm	}
401178825Sdfr	ret = kadm5_get_principals(kadm_handle, expression, &princs, &n_princs);
402178825Sdfr	free(expression);
40355682Smarkm	krb5_storage_free(sp);
40455682Smarkm	sp = krb5_storage_emem();
40555682Smarkm	krb5_store_int32(sp, ret);
40655682Smarkm	if(ret == 0){
40755682Smarkm	    int i;
40855682Smarkm	    krb5_store_int32(sp, n_princs);
40955682Smarkm	    for(i = 0; i < n_princs; i++)
41055682Smarkm		krb5_store_string(sp, princs[i]);
41155682Smarkm	    kadm5_free_name_list(kadm_handle, princs, &n_princs);
41255682Smarkm	}
41355682Smarkm	break;
41455682Smarkm    }
41555682Smarkm    default:
41655682Smarkm	krb5_warnx(context->context, "%s: UNKNOWN OP %d", client, cmd);
41755682Smarkm	krb5_storage_free(sp);
41855682Smarkm	sp = krb5_storage_emem();
41955682Smarkm	krb5_store_int32(sp, KADM5_FAILURE);
42055682Smarkm	break;
42155682Smarkm    }
42255682Smarkm    krb5_storage_to_data(sp, out);
42355682Smarkm    krb5_storage_free(sp);
42455682Smarkm    return 0;
42555682Smarkmfail:
42655682Smarkm    krb5_warn(context->context, ret, "%s", op);
427102644Snectar    krb5_storage_seek(sp, 0, SEEK_SET);
42855682Smarkm    krb5_store_int32(sp, ret);
42955682Smarkm    krb5_storage_to_data(sp, out);
43055682Smarkm    krb5_storage_free(sp);
43155682Smarkm    return 0;
43255682Smarkm}
43355682Smarkm
43455682Smarkmstatic void
43555682Smarkmv5_loop (krb5_context context,
43655682Smarkm	 krb5_auth_context ac,
43755682Smarkm	 krb5_boolean initial,
43855682Smarkm	 void *kadm_handle,
43955682Smarkm	 int fd)
44055682Smarkm{
44155682Smarkm    krb5_error_code ret;
44272445Sassar    krb5_data in, out;
44355682Smarkm
44455682Smarkm    for (;;) {
44572445Sassar	doing_useful_work = 0;
44672445Sassar	if(term_flag)
44772445Sassar	    exit(0);
44872445Sassar	ret = krb5_read_priv_message(context, ac, &fd, &in);
44972445Sassar	if(ret == HEIM_ERR_EOF)
45072445Sassar	    exit(0);
45172445Sassar	if(ret)
45272445Sassar	    krb5_err(context, 1, ret, "krb5_read_priv_message");
45372445Sassar	doing_useful_work = 1;
45472445Sassar	kadmind_dispatch(kadm_handle, initial, &in, &out);
45555682Smarkm	krb5_data_free(&in);
45672445Sassar	ret = krb5_write_priv_message(context, ac, &fd, &out);
45772445Sassar	if(ret)
45872445Sassar	    krb5_err(context, 1, ret, "krb5_write_priv_message");
45955682Smarkm    }
46055682Smarkm}
46155682Smarkm
46255682Smarkmstatic krb5_boolean
463103423Snectarmatch_appl_version(const void *data, const char *appl_version)
46455682Smarkm{
46555682Smarkm    unsigned minor;
46655682Smarkm    if(sscanf(appl_version, "KADM0.%u", &minor) != 1)
46755682Smarkm	return 0;
46855682Smarkm    *(unsigned*)data = minor;
46955682Smarkm    return 1;
47055682Smarkm}
47155682Smarkm
47255682Smarkmstatic void
47355682Smarkmhandle_v5(krb5_context context,
47455682Smarkm	  krb5_auth_context ac,
47555682Smarkm	  krb5_keytab keytab,
47655682Smarkm	  int len,
47755682Smarkm	  int fd)
47855682Smarkm{
47955682Smarkm    krb5_error_code ret;
48055682Smarkm    u_char version[sizeof(KRB5_SENDAUTH_VERSION)];
48155682Smarkm    krb5_ticket *ticket;
48272445Sassar    char *server_name;
48355682Smarkm    char *client;
48455682Smarkm    void *kadm_handle;
48555682Smarkm    ssize_t n;
48655682Smarkm    krb5_boolean initial;
48755682Smarkm
48855682Smarkm    unsigned kadm_version;
48955682Smarkm    kadm5_config_params realm_params;
49055682Smarkm
49155682Smarkm    if (len != sizeof(KRB5_SENDAUTH_VERSION))
49255682Smarkm	krb5_errx(context, 1, "bad sendauth len %d", len);
49355682Smarkm    n = krb5_net_read(context, &fd, version, len);
49455682Smarkm    if (n < 0)
49555682Smarkm	krb5_err (context, 1, errno, "reading sendauth version");
49655682Smarkm    if (n == 0)
49755682Smarkm	krb5_errx (context, 1, "EOF reading sendauth version");
49855682Smarkm    if(memcmp(version, KRB5_SENDAUTH_VERSION, len) != 0)
49955682Smarkm	krb5_errx(context, 1, "bad sendauth version %.8s", version);
50055682Smarkm
50155682Smarkm    ret = krb5_recvauth_match_version(context, &ac, &fd,
50255682Smarkm				      match_appl_version, &kadm_version,
50372445Sassar				      NULL, KRB5_RECVAUTH_IGNORE_VERSION,
50455682Smarkm				      keytab, &ticket);
50572445Sassar    if(ret == KRB5_KT_NOTFOUND)
50690926Snectar	krb5_errx(context, 1, "krb5_recvauth: key not found");
50755682Smarkm    if(ret)
50855682Smarkm	krb5_err(context, 1, ret, "krb5_recvauth");
50955682Smarkm
51072445Sassar    ret = krb5_unparse_name (context, ticket->server, &server_name);
51172445Sassar    if (ret)
51272445Sassar	krb5_err (context, 1, ret, "krb5_unparse_name");
51372445Sassar
51472445Sassar    if (strncmp (server_name, KADM5_ADMIN_SERVICE,
51572445Sassar		 strlen(KADM5_ADMIN_SERVICE)) != 0)
51672445Sassar	krb5_errx (context, 1, "ticket for strange principal (%s)",
51772445Sassar		   server_name);
51872445Sassar
51972445Sassar    free (server_name);
52072445Sassar
52155682Smarkm    memset(&realm_params, 0, sizeof(realm_params));
52255682Smarkm
52355682Smarkm    if(kadm_version == 1) {
52472445Sassar	krb5_data params;
52572445Sassar	ret = krb5_read_priv_message(context, ac, &fd, &params);
52672445Sassar	if(ret)
52772445Sassar	    krb5_err(context, 1, ret, "krb5_read_priv_message");
52855682Smarkm	_kadm5_unmarshal_params(context, &params, &realm_params);
52955682Smarkm    }
53055682Smarkm
53155682Smarkm    initial = ticket->ticket.flags.initial;
53255682Smarkm    ret = krb5_unparse_name(context, ticket->client, &client);
53355682Smarkm    if (ret)
53455682Smarkm	krb5_err (context, 1, ret, "krb5_unparse_name");
53555682Smarkm    krb5_free_ticket (context, ticket);
53655682Smarkm    ret = kadm5_init_with_password_ctx(context,
53755682Smarkm				       client,
53855682Smarkm				       NULL,
53955682Smarkm				       KADM5_ADMIN_SERVICE,
54055682Smarkm				       &realm_params,
54155682Smarkm				       0, 0,
54255682Smarkm				       &kadm_handle);
54355682Smarkm    if(ret)
54455682Smarkm	krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
54555682Smarkm    v5_loop (context, ac, initial, kadm_handle, fd);
54655682Smarkm}
54755682Smarkm
54855682Smarkmkrb5_error_code
54955682Smarkmkadmind_loop(krb5_context context,
55055682Smarkm	     krb5_auth_context ac,
55155682Smarkm	     krb5_keytab keytab,
55255682Smarkm	     int fd)
55355682Smarkm{
55455682Smarkm    unsigned char tmp[4];
55555682Smarkm    ssize_t n;
55655682Smarkm    unsigned long len;
55755682Smarkm
55855682Smarkm    n = krb5_net_read(context, &fd, tmp, 4);
55955682Smarkm    if(n == 0)
56055682Smarkm	exit(0);
56155682Smarkm    if(n < 0)
56272445Sassar	krb5_err(context, 1, errno, "read");
56355682Smarkm    _krb5_get_int(tmp, &len, 4);
564178825Sdfr    /* this v4 test could probably also go away */
56555682Smarkm    if(len > 0xffff && (len & 0xffff) == ('K' << 8) + 'A') {
566178825Sdfr	unsigned char v4reply[] = {
567178825Sdfr	    0x00, 0x0c,
568178825Sdfr	    'K', 'Y', 'O', 'U', 'L', 'O', 'S', 'E',
569178825Sdfr	    0x95, 0xb7, 0xa7, 0x08 /* KADM_BAD_VER */
570178825Sdfr	};
571178825Sdfr	krb5_net_write(context, &fd, v4reply, sizeof(v4reply));
57255682Smarkm	krb5_errx(context, 1, "packet appears to be version 4");
57355682Smarkm    } else {
57455682Smarkm	handle_v5(context, ac, keytab, len, fd);
57555682Smarkm    }
57655682Smarkm    return 0;
57755682Smarkm}
578