(handle_v5): read sendauth version correctly.
[metze/heimdal/wip.git] / kadmin / server.c
1 /*
2  * Copyright (c) 1997-1999 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
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. 
16  *
17  * 3. All advertising materials mentioning features or use of this software 
18  *    must display the following acknowledgement: 
19  *      This product includes software developed by Kungliga Tekniska 
20  *      Högskolan and its contributors. 
21  *
22  * 4. Neither the name of the Institute nor the names of its contributors 
23  *    may be used to endorse or promote products derived from this software 
24  *    without specific prior written permission. 
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
36  * SUCH DAMAGE. 
37  */
38
39 #include "kadmin_locl.h"
40 #include <krb5-private.h>
41
42 RCSID("$Id$");
43
44 static kadm5_ret_t
45 kadmind_dispatch(void *kadm_handle, krb5_data *in, krb5_data *out)
46 {
47     kadm5_ret_t ret;
48     int32_t cmd, mask, tmp;
49     kadm5_server_context *context = kadm_handle;
50     char client[128], name[128], name2[128];
51     char *op = "";
52     krb5_principal princ, princ2;
53     kadm5_principal_ent_rec ent;
54     char *password, *exp;
55     krb5_keyblock *new_keys;
56     int n_keys;
57     char **princs;
58     int n_princs;
59     krb5_storage *sp;
60     
61     krb5_unparse_name_fixed(context->context, context->caller, 
62                             client, sizeof(client));
63     
64     sp = krb5_storage_from_data(in);
65
66     krb5_ret_int32(sp, &cmd);
67     switch(cmd){
68     case kadm_get:{
69         op = "GET";
70         ret = krb5_ret_principal(sp, &princ);
71         if(ret)
72             goto fail;
73         ret = krb5_ret_int32(sp, &mask);
74         if(ret){
75             krb5_free_principal(context->context, princ);
76             goto fail;
77         }
78         krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
79         krb5_warnx(context->context, "%s: %s %s", client, op, name);
80         ret = _kadm5_acl_check_permission(context, KADM5_PRIV_GET);
81         if(ret){
82             krb5_free_principal(context->context, princ);
83             goto fail;
84         }
85         ret = kadm5_get_principal(kadm_handle, princ, &ent, mask);
86         krb5_storage_free(sp);
87         sp = krb5_storage_emem();
88         krb5_store_int32(sp, ret);
89         if(ret == 0){
90             kadm5_store_principal_ent(sp, &ent);
91             kadm5_free_principal_ent(kadm_handle, &ent);
92         }
93         krb5_free_principal(context->context, princ);
94         break;
95     }
96     case kadm_delete:{
97         op = "DELETE";
98         ret = krb5_ret_principal(sp, &princ);
99         if(ret)
100             goto fail;
101         krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
102         krb5_warnx(context->context, "%s: %s %s", client, op, name);
103         ret = _kadm5_acl_check_permission(context, KADM5_PRIV_DELETE);
104         if(ret){
105             krb5_free_principal(context->context, princ);
106             goto fail;
107         }
108         ret = kadm5_delete_principal(kadm_handle, princ);
109         krb5_free_principal(context->context, princ);
110         krb5_storage_free(sp);
111         sp = krb5_storage_emem();
112         krb5_store_int32(sp, ret);
113         break;
114     }
115     case kadm_create:{
116         op = "CREATE";
117         ret = kadm5_ret_principal_ent(sp, &ent);
118         if(ret)
119             goto fail;
120         ret = krb5_ret_int32(sp, &mask);
121         if(ret){
122             kadm5_free_principal_ent(context->context, &ent);
123             goto fail;
124         }
125         ret = krb5_ret_string(sp, &password);
126         if(ret){
127             kadm5_free_principal_ent(context->context, &ent);
128             goto fail;
129         }
130         krb5_unparse_name_fixed(context->context, ent.principal, 
131                                 name, sizeof(name));
132         krb5_warnx(context->context, "%s: %s %s", client, op, name);
133         ret = _kadm5_acl_check_permission(context, KADM5_PRIV_ADD);
134         if(ret){
135             kadm5_free_principal_ent(context->context, &ent);
136             memset(password, 0, strlen(password));
137             free(password);
138             goto fail;
139         }
140         ret = kadm5_create_principal(kadm_handle, &ent, 
141                                      mask, password);
142         kadm5_free_principal_ent(kadm_handle, &ent);
143         memset(password, 0, strlen(password));
144         free(password);
145         krb5_storage_free(sp);
146         sp = krb5_storage_emem();
147         krb5_store_int32(sp, ret);
148         break;
149     }
150     case kadm_modify:{
151         op = "MODIFY";
152         ret = kadm5_ret_principal_ent(sp, &ent);
153         if(ret)
154             goto fail;
155         ret = krb5_ret_int32(sp, &mask);
156         if(ret){
157             kadm5_free_principal_ent(context, &ent);
158             goto fail;
159         }
160         krb5_unparse_name_fixed(context->context, ent.principal, 
161                                 name, sizeof(name));
162         krb5_warnx(context->context, "%s: %s %s", client, op, name);
163         ret = _kadm5_acl_check_permission(context, KADM5_PRIV_MODIFY);
164         if(ret){
165             kadm5_free_principal_ent(context, &ent);
166             goto fail;
167         }
168         ret = kadm5_modify_principal(kadm_handle, &ent, mask);
169         kadm5_free_principal_ent(kadm_handle, &ent);
170         krb5_storage_free(sp);
171         sp = krb5_storage_emem();
172         krb5_store_int32(sp, ret);
173         break;
174     }
175     case kadm_rename:{
176         op = "RENAME";
177         ret = krb5_ret_principal(sp, &princ);
178         if(ret)
179             goto fail;
180         ret = krb5_ret_principal(sp, &princ2);
181         if(ret){
182             krb5_free_principal(context->context, princ);
183             goto fail;
184         }
185         krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
186         krb5_unparse_name_fixed(context->context, princ2, name2, sizeof(name2));
187         krb5_warnx(context->context, "%s: %s %s -> %s", 
188                    client, op, name, name2);
189         ret = _kadm5_acl_check_permission(context, 
190                                           KADM5_PRIV_ADD|KADM5_PRIV_DELETE);
191         if(ret){
192             krb5_free_principal(context->context, princ);
193             goto fail;
194         }
195         ret = kadm5_rename_principal(kadm_handle, princ, princ2);
196         krb5_free_principal(context->context, princ);
197         krb5_free_principal(context->context, princ2);
198         krb5_storage_free(sp);
199         sp = krb5_storage_emem();
200         krb5_store_int32(sp, ret);
201         break;
202     }
203     case kadm_chpass:{
204         op = "CHPASS";
205         ret = krb5_ret_principal(sp, &princ);
206         if(ret)
207             goto fail;
208         ret = krb5_ret_string(sp, &password);
209         if(ret){
210             krb5_free_principal(context->context, princ);
211             goto fail;
212         }
213         krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
214         krb5_warnx(context->context, "%s: %s %s", client, op, name);
215 #if 0
216         /* anyone can change her/his own password */
217         /* but not until there is a way to ensure that the
218            authentication was done via an initial ticket request */
219         if(!krb5_principal_compare(context->context, context->caller, princ))
220             ret = KADM5_AUTH_INSUFFICIENT;
221         if(ret)
222 #endif
223             ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW);
224         if(ret){
225             krb5_free_principal(context->context, princ);
226             goto fail;
227         }
228         ret = kadm5_chpass_principal(kadm_handle, princ, password);
229         krb5_free_principal(context->context, princ);
230         memset(password, 0, strlen(password));
231         free(password);
232         krb5_storage_free(sp);
233         sp = krb5_storage_emem();
234         krb5_store_int32(sp, ret);
235         break;
236     }
237     case kadm_randkey:{
238         op = "RANDKEY";
239         ret = krb5_ret_principal(sp, &princ);
240         if(ret)
241             goto fail;
242         krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
243         krb5_warnx(context->context, "%s: %s %s", client, op, name);
244 #if 0
245         /* anyone can change her/his own password */
246         /* but not until there is a way to ensure that the
247            authentication was done via an initial ticket request */
248         if(!krb5_principal_compare(context->context, context->caller, princ))
249             ret = KADM5_AUTH_INSUFFICIENT;
250         if(ret)
251 #endif
252             ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW);
253         if(ret){
254             krb5_free_principal(context->context, princ);
255             goto fail;
256         }
257         ret = kadm5_randkey_principal(kadm_handle, princ, 
258                                       &new_keys, &n_keys);
259         krb5_free_principal(context->context, princ);
260         krb5_storage_free(sp);
261         sp = krb5_storage_emem();
262         krb5_store_int32(sp, ret);
263         if(ret == 0){
264             int i;
265             krb5_store_int32(sp, n_keys);
266             for(i = 0; i < n_keys; i++){
267                 krb5_store_keyblock(sp, new_keys[i]);
268                 krb5_free_keyblock_contents(context->context, &new_keys[i]);
269             }
270         }
271         break;
272     }
273     case kadm_get_privs:{
274         ret = kadm5_get_privs(kadm_handle, &mask);
275         krb5_storage_free(sp);
276         sp = krb5_storage_emem();
277         krb5_store_int32(sp, ret);
278         if(ret == 0)
279             krb5_store_int32(sp, mask);
280         break;
281     }
282     case kadm_get_princs:{
283         op = "LIST";
284         ret = krb5_ret_int32(sp, &tmp);
285         if(ret)
286             goto fail;
287         if(tmp){
288             ret = krb5_ret_string(sp, &exp);
289             if(ret)
290                 goto fail;
291         }else
292             exp = NULL;
293         krb5_warnx(context->context, "%s: %s %s", client, op, exp ? exp : "*");
294         ret = _kadm5_acl_check_permission(context, KADM5_PRIV_LIST);
295         if(ret){
296             free(exp);
297             goto fail;
298         }
299         ret = kadm5_get_principals(kadm_handle, exp, &princs, &n_princs);
300         free(exp);
301         krb5_storage_free(sp);
302         sp = krb5_storage_emem();
303         krb5_store_int32(sp, ret);
304         if(ret == 0){
305             int i;
306             krb5_store_int32(sp, n_princs);
307             for(i = 0; i < n_princs; i++)
308                 krb5_store_string(sp, princs[i]);
309             kadm5_free_name_list(kadm_handle, princs, &n_princs);
310         }
311         break;
312     }
313     default:
314         krb5_warnx(context->context, "%s: UNKNOWN OP %d", client, cmd);
315         krb5_storage_free(sp);
316         sp = krb5_storage_emem();
317         krb5_store_int32(sp, KADM5_FAILURE);
318         break;
319     }
320     krb5_storage_to_data(sp, out);
321     krb5_storage_free(sp);
322     return 0;
323 fail:
324     krb5_warn(context->context, ret, "%s", op);
325     sp->seek(sp, 0, SEEK_SET);
326     krb5_store_int32(sp, ret);
327     krb5_storage_to_data(sp, out);
328     krb5_storage_free(sp);
329     return 0;
330 }
331
332 static void
333 v5_loop (krb5_context context,
334          krb5_auth_context ac,
335          void *kadm_handle,
336          int fd)
337 {
338     krb5_error_code ret;
339     ssize_t n;
340     unsigned long len;
341     u_char tmp[4];
342     struct iovec iov[2];
343     krb5_data in, out, msg, reply;
344
345     for (;;) {
346         krb5_net_read(context, &fd, tmp, 4);
347         _krb5_get_int (tmp, &len, 4);
348
349         in.length = len;
350         in.data = malloc(in.length);
351         n = krb5_net_read(context, &fd, in.data, in.length);
352         if (n == 0)
353             exit (0);
354         if(n < 0)
355             krb5_errx(context, 1, "read error: %d", errno);
356         if(n < in.length)
357             krb5_errx(context, 1, "short read (%ld)", (long int)n);
358         ret = krb5_rd_priv(context, ac, &in, &out, NULL);
359         krb5_data_free(&in);
360         kadmind_dispatch(kadm_handle, &out, &msg);
361         krb5_data_free(&out);
362         ret = krb5_mk_priv(context, ac, &msg, &reply, NULL);
363         krb5_data_free(&msg);
364         if(ret) 
365             krb5_err(context, 1, ret, "krb5_mk_priv");
366
367         _krb5_put_int(tmp, reply.length, 4);
368
369         iov[0].iov_base = tmp;
370         iov[0].iov_len  = 4;
371         iov[1].iov_base = reply.data;
372         iov[1].iov_len  = reply.length;
373         n = writev(fd, iov, 2);
374         krb5_data_free(&reply);
375         if(n < 0)
376             krb5_err(context, 1, errno, "writev");
377         if(n < iov[0].iov_len + iov[1].iov_len)
378             krb5_errx(context, 1, "short write");
379     }
380 }
381
382 static void
383 handle_v5(krb5_context context,
384           krb5_auth_context ac,
385           krb5_keytab keytab,
386           int len,
387           int fd)
388 {
389     krb5_error_code ret;
390     u_char version[sizeof(KRB5_SENDAUTH_VERSION)];
391     krb5_ticket *ticket;
392     krb5_principal server;
393     char *client;
394     void *kadm_handle;
395
396     if (len != sizeof(KRB5_SENDAUTH_VERSION))
397         krb5_errx(context, 1, "bad sendauth len %d", len);
398     if(krb5_net_read(context, &fd, version, len) != len)
399         krb5_err (context, 1, errno, "reading sendauth version");
400     if(memcmp(version, KRB5_SENDAUTH_VERSION, len) != 0)
401         krb5_errx(context, 1, "bad sendauth version %.8s", version);
402         
403     ret = krb5_parse_name(context, KADM5_ADMIN_SERVICE, &server);
404     if (ret)
405         krb5_err (context, 1, ret, "krb5_parse_name %s", KADM5_ADMIN_SERVICE);
406     ret = krb5_recvauth(context, &ac, &fd, KADMIN_APPL_VERSION, 
407                         server, KRB5_RECVAUTH_IGNORE_VERSION, 
408                         keytab, &ticket);
409     krb5_free_principal(context, server);
410             
411     if(ret)
412         krb5_err(context, 1, ret, "krb5_recvauth");
413     ret = krb5_unparse_name(context, ticket->client, &client);
414     if (ret)
415         krb5_err (context, 1, ret, "krb5_unparse_name");
416     ret = kadm5_init_with_password_ctx(context, 
417                                        client, 
418                                        NULL,
419                                        KADM5_ADMIN_SERVICE,
420                                        NULL, 0, 0, 
421                                        &kadm_handle);
422     if(ret)
423         krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
424     v5_loop (context, ac, kadm_handle, fd);
425 }
426
427 krb5_error_code
428 kadmind_loop(krb5_context context,
429              krb5_auth_context ac,
430              krb5_keytab keytab, 
431              int fd)
432 {
433     unsigned char tmp[4];
434     ssize_t n;
435     unsigned long len;
436
437     n = krb5_net_read(context, &fd, tmp, 4);
438     if(n == 0)
439         exit(0);
440     if(n < 0)
441         krb5_errx(context, 1, "read error: %d", errno);
442     if(n < 4)
443         krb5_errx(context, 1, "short read (%ld)", (long int)n);
444     _krb5_get_int(tmp, &len, 4);
445     if(len > 0xffff && (len & 0xffff) == ('K' << 8) + 'A') {
446         len >>= 16;
447 #ifdef KRB4
448         handle_v4(context, len, fd);
449 #else
450         krb5_errx(context, 1, "packet appears to be version 4");
451 #endif
452     } else {
453         handle_v5(context, ac, keytab, len, fd);
454     }
455     return 0;
456 }