155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kadmin_locl.h"
3555682Smarkm#include <krb5-private.h>
3655682Smarkm
3755682Smarkmstatic kadm5_ret_t
38233294Sstaskadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
3955682Smarkm		 krb5_data *in, krb5_data *out)
4055682Smarkm{
4155682Smarkm    kadm5_ret_t ret;
4255682Smarkm    int32_t cmd, mask, tmp;
43233294Sstas    kadm5_server_context *contextp = kadm_handlep;
4455682Smarkm    char client[128], name[128], name2[128];
45233294Sstas    const char *op = "";
4655682Smarkm    krb5_principal princ, princ2;
4755682Smarkm    kadm5_principal_ent_rec ent;
48178825Sdfr    char *password, *expression;
4955682Smarkm    krb5_keyblock *new_keys;
5055682Smarkm    int n_keys;
5155682Smarkm    char **princs;
5255682Smarkm    int n_princs;
5355682Smarkm    krb5_storage *sp;
54233294Sstas
55233294Sstas    krb5_unparse_name_fixed(contextp->context, contextp->caller,
5655682Smarkm			    client, sizeof(client));
57233294Sstas
5855682Smarkm    sp = krb5_storage_from_data(in);
59233294Sstas    if (sp == NULL)
60233294Sstas	krb5_errx(contextp->context, 1, "out of memory");
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){
71233294Sstas	    krb5_free_principal(contextp->context, princ);
7255682Smarkm	    goto fail;
7355682Smarkm	}
74233294Sstas	mask |= KADM5_PRINCIPAL;
75233294Sstas	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
76233294Sstas	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
77233294Sstas	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
7855682Smarkm	if(ret){
79233294Sstas	    krb5_free_principal(contextp->context, princ);
8055682Smarkm	    goto fail;
8155682Smarkm	}
82233294Sstas	ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask);
8355682Smarkm	krb5_storage_free(sp);
8455682Smarkm	sp = krb5_storage_emem();
8555682Smarkm	krb5_store_int32(sp, ret);
8655682Smarkm	if(ret == 0){
8755682Smarkm	    kadm5_store_principal_ent(sp, &ent);
88233294Sstas	    kadm5_free_principal_ent(kadm_handlep, &ent);
8955682Smarkm	}
90233294Sstas	krb5_free_principal(contextp->context, princ);
9155682Smarkm	break;
9255682Smarkm    }
9355682Smarkm    case kadm_delete:{
9455682Smarkm	op = "DELETE";
9555682Smarkm	ret = krb5_ret_principal(sp, &princ);
9655682Smarkm	if(ret)
9755682Smarkm	    goto fail;
98233294Sstas	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
99233294Sstas	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
100233294Sstas	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
10155682Smarkm	if(ret){
102233294Sstas	    krb5_free_principal(contextp->context, princ);
10355682Smarkm	    goto fail;
10455682Smarkm	}
105233294Sstas	ret = kadm5_delete_principal(kadm_handlep, princ);
106233294Sstas	krb5_free_principal(contextp->context, princ);
10755682Smarkm	krb5_storage_free(sp);
10855682Smarkm	sp = krb5_storage_emem();
10955682Smarkm	krb5_store_int32(sp, ret);
11055682Smarkm	break;
11155682Smarkm    }
11255682Smarkm    case kadm_create:{
11355682Smarkm	op = "CREATE";
11455682Smarkm	ret = kadm5_ret_principal_ent(sp, &ent);
11555682Smarkm	if(ret)
11655682Smarkm	    goto fail;
11755682Smarkm	ret = krb5_ret_int32(sp, &mask);
11855682Smarkm	if(ret){
119233294Sstas	    kadm5_free_principal_ent(contextp->context, &ent);
12055682Smarkm	    goto fail;
12155682Smarkm	}
12255682Smarkm	ret = krb5_ret_string(sp, &password);
12355682Smarkm	if(ret){
124233294Sstas	    kadm5_free_principal_ent(contextp->context, &ent);
12555682Smarkm	    goto fail;
12655682Smarkm	}
127233294Sstas	krb5_unparse_name_fixed(contextp->context, ent.principal,
12855682Smarkm				name, sizeof(name));
129233294Sstas	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
130233294Sstas	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD,
13172445Sassar					  ent.principal);
13255682Smarkm	if(ret){
133233294Sstas	    kadm5_free_principal_ent(contextp->context, &ent);
13455682Smarkm	    memset(password, 0, strlen(password));
13555682Smarkm	    free(password);
13655682Smarkm	    goto fail;
13755682Smarkm	}
138233294Sstas	ret = kadm5_create_principal(kadm_handlep, &ent,
13955682Smarkm				     mask, password);
140233294Sstas	kadm5_free_principal_ent(kadm_handlep, &ent);
14155682Smarkm	memset(password, 0, strlen(password));
14255682Smarkm	free(password);
14355682Smarkm	krb5_storage_free(sp);
14455682Smarkm	sp = krb5_storage_emem();
14555682Smarkm	krb5_store_int32(sp, ret);
14655682Smarkm	break;
14755682Smarkm    }
14855682Smarkm    case kadm_modify:{
14955682Smarkm	op = "MODIFY";
15055682Smarkm	ret = kadm5_ret_principal_ent(sp, &ent);
15155682Smarkm	if(ret)
15255682Smarkm	    goto fail;
15355682Smarkm	ret = krb5_ret_int32(sp, &mask);
15455682Smarkm	if(ret){
155233294Sstas	    kadm5_free_principal_ent(contextp, &ent);
15655682Smarkm	    goto fail;
15755682Smarkm	}
158233294Sstas	krb5_unparse_name_fixed(contextp->context, ent.principal,
15955682Smarkm				name, sizeof(name));
160233294Sstas	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
161233294Sstas	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_MODIFY,
16272445Sassar					  ent.principal);
16355682Smarkm	if(ret){
164233294Sstas	    kadm5_free_principal_ent(contextp, &ent);
16555682Smarkm	    goto fail;
16655682Smarkm	}
167233294Sstas	ret = kadm5_modify_principal(kadm_handlep, &ent, mask);
168233294Sstas	kadm5_free_principal_ent(kadm_handlep, &ent);
16955682Smarkm	krb5_storage_free(sp);
17055682Smarkm	sp = krb5_storage_emem();
17155682Smarkm	krb5_store_int32(sp, ret);
17255682Smarkm	break;
17355682Smarkm    }
17455682Smarkm    case kadm_rename:{
17555682Smarkm	op = "RENAME";
17655682Smarkm	ret = krb5_ret_principal(sp, &princ);
17755682Smarkm	if(ret)
17855682Smarkm	    goto fail;
17955682Smarkm	ret = krb5_ret_principal(sp, &princ2);
18055682Smarkm	if(ret){
181233294Sstas	    krb5_free_principal(contextp->context, princ);
18255682Smarkm	    goto fail;
18355682Smarkm	}
184233294Sstas	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
185233294Sstas	krb5_unparse_name_fixed(contextp->context, princ2, name2, sizeof(name2));
186233294Sstas	krb5_warnx(contextp->context, "%s: %s %s -> %s",
18755682Smarkm		   client, op, name, name2);
188233294Sstas	ret = _kadm5_acl_check_permission(contextp,
18972445Sassar					  KADM5_PRIV_ADD,
19072445Sassar					  princ2)
191233294Sstas	    || _kadm5_acl_check_permission(contextp,
19272445Sassar					   KADM5_PRIV_DELETE,
19372445Sassar					   princ);
19455682Smarkm	if(ret){
195233294Sstas	    krb5_free_principal(contextp->context, princ);
196233294Sstas	    krb5_free_principal(contextp->context, princ2);
19755682Smarkm	    goto fail;
19855682Smarkm	}
199233294Sstas	ret = kadm5_rename_principal(kadm_handlep, princ, princ2);
200233294Sstas	krb5_free_principal(contextp->context, princ);
201233294Sstas	krb5_free_principal(contextp->context, princ2);
20255682Smarkm	krb5_storage_free(sp);
20355682Smarkm	sp = krb5_storage_emem();
20455682Smarkm	krb5_store_int32(sp, ret);
20555682Smarkm	break;
20655682Smarkm    }
20755682Smarkm    case kadm_chpass:{
20855682Smarkm	op = "CHPASS";
20955682Smarkm	ret = krb5_ret_principal(sp, &princ);
21055682Smarkm	if(ret)
21155682Smarkm	    goto fail;
21255682Smarkm	ret = krb5_ret_string(sp, &password);
21355682Smarkm	if(ret){
214233294Sstas	    krb5_free_principal(contextp->context, princ);
21555682Smarkm	    goto fail;
21655682Smarkm	}
217233294Sstas	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
218233294Sstas	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
21955682Smarkm
22055682Smarkm	/*
22155682Smarkm	 * The change is allowed if at least one of:
222233294Sstas	 *
223233294Sstas	 * a) allowed by sysadmin
224233294Sstas	 * b) it's for the principal him/herself and this was an
225120945Snectar	 *    initial ticket, but then, check with the password quality
226120945Snectar	 *    function.
227233294Sstas	 * c) the user is on the CPW ACL.
22855682Smarkm	 */
22955682Smarkm
230233294Sstas	if (krb5_config_get_bool_default(contextp->context, NULL, TRUE,
231233294Sstas					 "kadmin", "allow_self_change_password", NULL)
232233294Sstas	    && initial
233233294Sstas	    && krb5_principal_compare (contextp->context, contextp->caller,
23455682Smarkm				       princ))
235120945Snectar	{
236120945Snectar	    krb5_data pwd_data;
237120945Snectar	    const char *pwd_reason;
238120945Snectar
239120945Snectar	    pwd_data.data = password;
240120945Snectar	    pwd_data.length = strlen(password);
241120945Snectar
242233294Sstas	    pwd_reason = kadm5_check_password_quality (contextp->context,
243120945Snectar						       princ, &pwd_data);
244120945Snectar	    if (pwd_reason != NULL)
245120945Snectar		ret = KADM5_PASS_Q_DICT;
246120945Snectar	    else
247120945Snectar		ret = 0;
248120945Snectar	} else
249233294Sstas	    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
25055682Smarkm
25155682Smarkm	if(ret) {
252233294Sstas	    krb5_free_principal(contextp->context, princ);
253120945Snectar	    memset(password, 0, strlen(password));
254120945Snectar	    free(password);
25555682Smarkm	    goto fail;
25655682Smarkm	}
257233294Sstas	ret = kadm5_chpass_principal(kadm_handlep, princ, password);
258233294Sstas	krb5_free_principal(contextp->context, princ);
25955682Smarkm	memset(password, 0, strlen(password));
26055682Smarkm	free(password);
26155682Smarkm	krb5_storage_free(sp);
26255682Smarkm	sp = krb5_storage_emem();
26355682Smarkm	krb5_store_int32(sp, ret);
26455682Smarkm	break;
26555682Smarkm    }
26672445Sassar    case kadm_chpass_with_key:{
26772445Sassar	int i;
26872445Sassar	krb5_key_data *key_data;
26972445Sassar	int n_key_data;
27072445Sassar
27172445Sassar	op = "CHPASS_WITH_KEY";
27272445Sassar	ret = krb5_ret_principal(sp, &princ);
27372445Sassar	if(ret)
27472445Sassar	    goto fail;
27572445Sassar	ret = krb5_ret_int32(sp, &n_key_data);
27672445Sassar	if (ret) {
277233294Sstas	    krb5_free_principal(contextp->context, princ);
27872445Sassar	    goto fail;
27972445Sassar	}
280103423Snectar	/* n_key_data will be squeezed into an int16_t below. */
281103423Snectar	if (n_key_data < 0 || n_key_data >= 1 << 16 ||
282233294Sstas	    (size_t)n_key_data > UINT_MAX/sizeof(*key_data)) {
283103423Snectar	    ret = ERANGE;
284233294Sstas	    krb5_free_principal(contextp->context, princ);
285103423Snectar	    goto fail;
286103423Snectar	}
28772445Sassar
28872445Sassar	key_data = malloc (n_key_data * sizeof(*key_data));
289233294Sstas	if (key_data == NULL && n_key_data != 0) {
29072445Sassar	    ret = ENOMEM;
291233294Sstas	    krb5_free_principal(contextp->context, princ);
29272445Sassar	    goto fail;
29372445Sassar	}
29472445Sassar
29572445Sassar	for (i = 0; i < n_key_data; ++i) {
29672445Sassar	    ret = kadm5_ret_key_data (sp, &key_data[i]);
29772445Sassar	    if (ret) {
29872445Sassar		int16_t dummy = i;
29972445Sassar
300233294Sstas		kadm5_free_key_data (contextp, &dummy, key_data);
30172445Sassar		free (key_data);
302233294Sstas		krb5_free_principal(contextp->context, princ);
30372445Sassar		goto fail;
30472445Sassar	    }
30572445Sassar	}
30672445Sassar
307233294Sstas	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
308233294Sstas	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
30972445Sassar
31072445Sassar	/*
311120945Snectar	 * The change is only allowed if the user is on the CPW ACL,
312120945Snectar	 * this it to force password quality check on the user.
31372445Sassar	 */
31472445Sassar
315233294Sstas	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
31672445Sassar	if(ret) {
31772445Sassar	    int16_t dummy = n_key_data;
31872445Sassar
319233294Sstas	    kadm5_free_key_data (contextp, &dummy, key_data);
32072445Sassar	    free (key_data);
321233294Sstas	    krb5_free_principal(contextp->context, princ);
32272445Sassar	    goto fail;
32372445Sassar	}
324233294Sstas	ret = kadm5_chpass_principal_with_key(kadm_handlep, princ,
32572445Sassar					      n_key_data, key_data);
32672445Sassar	{
32772445Sassar	    int16_t dummy = n_key_data;
328233294Sstas	    kadm5_free_key_data (contextp, &dummy, key_data);
32972445Sassar	}
33072445Sassar	free (key_data);
331233294Sstas	krb5_free_principal(contextp->context, princ);
33272445Sassar	krb5_storage_free(sp);
33372445Sassar	sp = krb5_storage_emem();
33472445Sassar	krb5_store_int32(sp, ret);
33572445Sassar	break;
33672445Sassar    }
33755682Smarkm    case kadm_randkey:{
33855682Smarkm	op = "RANDKEY";
33955682Smarkm	ret = krb5_ret_principal(sp, &princ);
34055682Smarkm	if(ret)
34155682Smarkm	    goto fail;
342233294Sstas	krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
343233294Sstas	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
34455682Smarkm	/*
34555682Smarkm	 * The change is allowed if at least one of:
34655682Smarkm	 * a) it's for the principal him/herself and this was an initial ticket
34755682Smarkm	 * b) the user is on the CPW ACL.
34855682Smarkm	 */
34955682Smarkm
35055682Smarkm	if (initial
351233294Sstas	    && krb5_principal_compare (contextp->context, contextp->caller,
35255682Smarkm				       princ))
35355682Smarkm	    ret = 0;
35455682Smarkm	else
355233294Sstas	    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
35655682Smarkm
35755682Smarkm	if(ret) {
358233294Sstas	    krb5_free_principal(contextp->context, princ);
35955682Smarkm	    goto fail;
36055682Smarkm	}
361233294Sstas	ret = kadm5_randkey_principal(kadm_handlep, princ,
36255682Smarkm				      &new_keys, &n_keys);
363233294Sstas	krb5_free_principal(contextp->context, princ);
36455682Smarkm	krb5_storage_free(sp);
36555682Smarkm	sp = krb5_storage_emem();
36655682Smarkm	krb5_store_int32(sp, ret);
36755682Smarkm	if(ret == 0){
36855682Smarkm	    int i;
36955682Smarkm	    krb5_store_int32(sp, n_keys);
37055682Smarkm	    for(i = 0; i < n_keys; i++){
37155682Smarkm		krb5_store_keyblock(sp, new_keys[i]);
372233294Sstas		krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
37355682Smarkm	    }
374233294Sstas	    free(new_keys);
37555682Smarkm	}
37655682Smarkm	break;
37755682Smarkm    }
37855682Smarkm    case kadm_get_privs:{
379178825Sdfr	uint32_t privs;
380233294Sstas	ret = kadm5_get_privs(kadm_handlep, &privs);
38155682Smarkm	krb5_storage_free(sp);
38255682Smarkm	sp = krb5_storage_emem();
38355682Smarkm	krb5_store_int32(sp, ret);
38455682Smarkm	if(ret == 0)
385178825Sdfr	    krb5_store_uint32(sp, privs);
38655682Smarkm	break;
38755682Smarkm    }
38855682Smarkm    case kadm_get_princs:{
38955682Smarkm	op = "LIST";
39055682Smarkm	ret = krb5_ret_int32(sp, &tmp);
39155682Smarkm	if(ret)
39255682Smarkm	    goto fail;
39355682Smarkm	if(tmp){
394178825Sdfr	    ret = krb5_ret_string(sp, &expression);
39555682Smarkm	    if(ret)
39655682Smarkm		goto fail;
39755682Smarkm	}else
398178825Sdfr	    expression = NULL;
399233294Sstas	krb5_warnx(contextp->context, "%s: %s %s", client, op,
400178825Sdfr		   expression ? expression : "*");
401233294Sstas	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_LIST, NULL);
40255682Smarkm	if(ret){
403178825Sdfr	    free(expression);
40455682Smarkm	    goto fail;
40555682Smarkm	}
406233294Sstas	ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs);
407178825Sdfr	free(expression);
40855682Smarkm	krb5_storage_free(sp);
40955682Smarkm	sp = krb5_storage_emem();
41055682Smarkm	krb5_store_int32(sp, ret);
41155682Smarkm	if(ret == 0){
41255682Smarkm	    int i;
41355682Smarkm	    krb5_store_int32(sp, n_princs);
41455682Smarkm	    for(i = 0; i < n_princs; i++)
41555682Smarkm		krb5_store_string(sp, princs[i]);
416233294Sstas	    kadm5_free_name_list(kadm_handlep, princs, &n_princs);
41755682Smarkm	}
41855682Smarkm	break;
41955682Smarkm    }
42055682Smarkm    default:
421233294Sstas	krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd);
42255682Smarkm	krb5_storage_free(sp);
42355682Smarkm	sp = krb5_storage_emem();
42455682Smarkm	krb5_store_int32(sp, KADM5_FAILURE);
42555682Smarkm	break;
42655682Smarkm    }
42755682Smarkm    krb5_storage_to_data(sp, out);
42855682Smarkm    krb5_storage_free(sp);
42955682Smarkm    return 0;
43055682Smarkmfail:
431233294Sstas    krb5_warn(contextp->context, ret, "%s", op);
432102644Snectar    krb5_storage_seek(sp, 0, SEEK_SET);
43355682Smarkm    krb5_store_int32(sp, ret);
43455682Smarkm    krb5_storage_to_data(sp, out);
43555682Smarkm    krb5_storage_free(sp);
43655682Smarkm    return 0;
43755682Smarkm}
43855682Smarkm
43955682Smarkmstatic void
440233294Sstasv5_loop (krb5_context contextp,
44155682Smarkm	 krb5_auth_context ac,
44255682Smarkm	 krb5_boolean initial,
443233294Sstas	 void *kadm_handlep,
444233294Sstas	 krb5_socket_t fd)
44555682Smarkm{
44655682Smarkm    krb5_error_code ret;
44772445Sassar    krb5_data in, out;
44855682Smarkm
44955682Smarkm    for (;;) {
45072445Sassar	doing_useful_work = 0;
45172445Sassar	if(term_flag)
45272445Sassar	    exit(0);
453233294Sstas	ret = krb5_read_priv_message(contextp, ac, &fd, &in);
45472445Sassar	if(ret == HEIM_ERR_EOF)
45572445Sassar	    exit(0);
45672445Sassar	if(ret)
457233294Sstas	    krb5_err(contextp, 1, ret, "krb5_read_priv_message");
45872445Sassar	doing_useful_work = 1;
459233294Sstas	kadmind_dispatch(kadm_handlep, initial, &in, &out);
46055682Smarkm	krb5_data_free(&in);
461233294Sstas	ret = krb5_write_priv_message(contextp, ac, &fd, &out);
46272445Sassar	if(ret)
463233294Sstas	    krb5_err(contextp, 1, ret, "krb5_write_priv_message");
46455682Smarkm    }
46555682Smarkm}
46655682Smarkm
46755682Smarkmstatic krb5_boolean
468103423Snectarmatch_appl_version(const void *data, const char *appl_version)
46955682Smarkm{
47055682Smarkm    unsigned minor;
47155682Smarkm    if(sscanf(appl_version, "KADM0.%u", &minor) != 1)
47255682Smarkm	return 0;
473233294Sstas    /*XXX*/
474233294Sstas    *(unsigned*)(intptr_t)data = minor;
47555682Smarkm    return 1;
47655682Smarkm}
47755682Smarkm
47855682Smarkmstatic void
479233294Sstashandle_v5(krb5_context contextp,
48055682Smarkm	  krb5_keytab keytab,
481233294Sstas	  krb5_socket_t fd)
48255682Smarkm{
48355682Smarkm    krb5_error_code ret;
48455682Smarkm    krb5_ticket *ticket;
48572445Sassar    char *server_name;
48655682Smarkm    char *client;
487233294Sstas    void *kadm_handlep;
48855682Smarkm    krb5_boolean initial;
489233294Sstas    krb5_auth_context ac = NULL;
49055682Smarkm
49155682Smarkm    unsigned kadm_version;
49255682Smarkm    kadm5_config_params realm_params;
49355682Smarkm
494233294Sstas    ret = krb5_recvauth_match_version(contextp, &ac, &fd,
49555682Smarkm				      match_appl_version, &kadm_version,
496233294Sstas				      NULL, KRB5_RECVAUTH_IGNORE_VERSION,
49755682Smarkm				      keytab, &ticket);
498233294Sstas    if (ret)
499233294Sstas	krb5_err(contextp, 1, ret, "krb5_recvauth");
50055682Smarkm
501233294Sstas    ret = krb5_unparse_name (contextp, ticket->server, &server_name);
50272445Sassar    if (ret)
503233294Sstas	krb5_err (contextp, 1, ret, "krb5_unparse_name");
50472445Sassar
50572445Sassar    if (strncmp (server_name, KADM5_ADMIN_SERVICE,
50672445Sassar		 strlen(KADM5_ADMIN_SERVICE)) != 0)
507233294Sstas	krb5_errx (contextp, 1, "ticket for strange principal (%s)",
50872445Sassar		   server_name);
50972445Sassar
51072445Sassar    free (server_name);
51172445Sassar
51255682Smarkm    memset(&realm_params, 0, sizeof(realm_params));
51355682Smarkm
51455682Smarkm    if(kadm_version == 1) {
51572445Sassar	krb5_data params;
516233294Sstas	ret = krb5_read_priv_message(contextp, ac, &fd, &params);
51772445Sassar	if(ret)
518233294Sstas	    krb5_err(contextp, 1, ret, "krb5_read_priv_message");
519233294Sstas	_kadm5_unmarshal_params(contextp, &params, &realm_params);
52055682Smarkm    }
52155682Smarkm
52255682Smarkm    initial = ticket->ticket.flags.initial;
523233294Sstas    ret = krb5_unparse_name(contextp, ticket->client, &client);
52455682Smarkm    if (ret)
525233294Sstas	krb5_err (contextp, 1, ret, "krb5_unparse_name");
526233294Sstas    krb5_free_ticket (contextp, ticket);
527233294Sstas    ret = kadm5_s_init_with_password_ctx(contextp,
528233294Sstas					 client,
529233294Sstas					 NULL,
530233294Sstas					 KADM5_ADMIN_SERVICE,
531233294Sstas					 &realm_params,
532233294Sstas					 0, 0,
533233294Sstas					 &kadm_handlep);
53455682Smarkm    if(ret)
535233294Sstas	krb5_err (contextp, 1, ret, "kadm5_init_with_password_ctx");
536233294Sstas    v5_loop (contextp, ac, initial, kadm_handlep, fd);
53755682Smarkm}
53855682Smarkm
53955682Smarkmkrb5_error_code
540233294Sstaskadmind_loop(krb5_context contextp,
541233294Sstas	     krb5_keytab keytab,
542233294Sstas	     krb5_socket_t sock)
54355682Smarkm{
544233294Sstas    u_char buf[sizeof(KRB5_SENDAUTH_VERSION) + 4];
54555682Smarkm    ssize_t n;
54655682Smarkm    unsigned long len;
54755682Smarkm
548233294Sstas    n = krb5_net_read(contextp, &sock, buf, 4);
54955682Smarkm    if(n == 0)
55055682Smarkm	exit(0);
55155682Smarkm    if(n < 0)
552233294Sstas	krb5_err(contextp, 1, errno, "read");
553233294Sstas    _krb5_get_int(buf, &len, 4);
554233294Sstas
555233294Sstas    if (len == sizeof(KRB5_SENDAUTH_VERSION)) {
556233294Sstas
557233294Sstas	n = krb5_net_read(contextp, &sock, buf + 4, len);
558233294Sstas	if (n < 0)
559233294Sstas	    krb5_err (contextp, 1, errno, "reading sendauth version");
560233294Sstas	if (n == 0)
561233294Sstas	    krb5_errx (contextp, 1, "EOF reading sendauth version");
562233294Sstas
563233294Sstas	if(memcmp(buf + 4, KRB5_SENDAUTH_VERSION, len) == 0) {
564233294Sstas	    handle_v5(contextp, keytab, sock);
565233294Sstas	    return 0;
566233294Sstas	}
567233294Sstas	len += 4;
568233294Sstas    } else
569233294Sstas	len = 4;
570233294Sstas
571233294Sstas    handle_mit(contextp, buf, len, sock);
572233294Sstas
57355682Smarkm    return 0;
57455682Smarkm}
575