Release 0.0r
[heimdal.git] / kadmin / server.c
blob6ed98d26b4930ff43947df31dadcde2b2fcefaf7
1 /*
2 * Copyright (c) 1997, 1998 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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. 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.
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.
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.
39 #include "kadm5_locl.h"
41 RCSID("$Id$");
43 static kadm5_ret_t
44 kadmind_dispatch(void *kadm_handle, krb5_data *in, krb5_data *out)
46 kadm5_ret_t ret;
47 int32_t cmd, mask, tmp;
48 kadm5_server_context *context = kadm_handle;
49 char client[128], name[128], name2[128];
50 char *op = "";
51 krb5_principal princ, princ2;
52 kadm5_principal_ent_rec ent;
53 char *password, *exp;
54 krb5_keyblock *new_keys;
55 int n_keys;
56 char **princs;
57 int n_princs;
58 krb5_storage *sp;
60 krb5_unparse_name_fixed(context->context, context->caller,
61 client, sizeof(client));
63 sp = krb5_storage_from_data(in);
65 krb5_ret_int32(sp, &cmd);
66 switch(cmd){
67 case kadm_get:{
68 op = "GET";
69 ret = krb5_ret_principal(sp, &princ);
70 if(ret)
71 goto fail;
72 ret = krb5_ret_int32(sp, &mask);
73 if(ret){
74 krb5_free_principal(context->context, princ);
75 goto fail;
77 krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
78 krb5_warnx(context->context, "%s: %s %s", client, op, name);
79 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_GET);
80 if(ret){
81 krb5_free_principal(context->context, princ);
82 goto fail;
84 ret = kadm5_get_principal(kadm_handle, princ, &ent, mask);
85 krb5_storage_free(sp);
86 sp = krb5_storage_emem();
87 krb5_store_int32(sp, ret);
88 if(ret == 0){
89 kadm5_store_principal_ent(sp, &ent);
90 kadm5_free_principal_ent(kadm_handle, &ent);
92 krb5_free_principal(context->context, princ);
93 break;
95 case kadm_delete:{
96 op = "DELETE";
97 ret = krb5_ret_principal(sp, &princ);
98 if(ret)
99 goto fail;
100 krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
101 krb5_warnx(context->context, "%s: %s %s", client, op, name);
102 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_DELETE);
103 if(ret){
104 krb5_free_principal(context->context, princ);
105 goto fail;
107 ret = kadm5_delete_principal(kadm_handle, princ);
108 krb5_free_principal(context->context, princ);
109 krb5_storage_free(sp);
110 sp = krb5_storage_emem();
111 krb5_store_int32(sp, ret);
112 break;
114 case kadm_create:{
115 op = "CREATE";
116 ret = kadm5_ret_principal_ent(sp, &ent);
117 if(ret)
118 goto fail;
119 ret = krb5_ret_int32(sp, &mask);
120 if(ret){
121 kadm5_free_principal_ent(context->context, &ent);
122 goto fail;
124 ret = krb5_ret_string(sp, &password);
125 if(ret){
126 kadm5_free_principal_ent(context->context, &ent);
127 goto fail;
129 krb5_unparse_name_fixed(context->context, ent.principal,
130 name, sizeof(name));
131 krb5_warnx(context->context, "%s: %s %s", client, op, name);
132 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_ADD);
133 if(ret){
134 kadm5_free_principal_ent(context->context, &ent);
135 memset(password, 0, strlen(password));
136 free(password);
137 goto fail;
139 ret = kadm5_create_principal(kadm_handle, &ent,
140 mask, password);
141 kadm5_free_principal_ent(kadm_handle, &ent);
142 memset(password, 0, strlen(password));
143 free(password);
144 krb5_storage_free(sp);
145 sp = krb5_storage_emem();
146 krb5_store_int32(sp, ret);
147 break;
149 case kadm_modify:{
150 op = "MODIFY";
151 ret = kadm5_ret_principal_ent(sp, &ent);
152 if(ret)
153 goto fail;
154 ret = krb5_ret_int32(sp, &mask);
155 if(ret){
156 kadm5_free_principal_ent(context, &ent);
157 goto fail;
159 krb5_unparse_name_fixed(context->context, ent.principal,
160 name, sizeof(name));
161 krb5_warnx(context->context, "%s: %s %s", client, op, name);
162 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_MODIFY);
163 if(ret){
164 kadm5_free_principal_ent(context, &ent);
165 goto fail;
167 ret = kadm5_modify_principal(kadm_handle, &ent, mask);
168 kadm5_free_principal_ent(kadm_handle, &ent);
169 krb5_storage_free(sp);
170 sp = krb5_storage_emem();
171 krb5_store_int32(sp, ret);
172 break;
174 case kadm_rename:{
175 op = "RENAME";
176 ret = krb5_ret_principal(sp, &princ);
177 if(ret)
178 goto fail;
179 ret = krb5_ret_principal(sp, &princ2);
180 if(ret){
181 krb5_free_principal(context->context, princ);
182 goto fail;
184 krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
185 krb5_unparse_name_fixed(context->context, princ2, name2, sizeof(name2));
186 krb5_warnx(context->context, "%s: %s %s -> %s",
187 client, op, name, name2);
188 ret = _kadm5_acl_check_permission(context,
189 KADM5_PRIV_ADD|KADM5_PRIV_DELETE);
190 if(ret){
191 krb5_free_principal(context->context, princ);
192 goto fail;
194 ret = kadm5_rename_principal(kadm_handle, princ, princ2);
195 krb5_free_principal(context->context, princ);
196 krb5_free_principal(context->context, princ2);
197 krb5_storage_free(sp);
198 sp = krb5_storage_emem();
199 krb5_store_int32(sp, ret);
200 break;
202 case kadm_chpass:{
203 op = "CHPASS";
204 ret = krb5_ret_principal(sp, &princ);
205 if(ret)
206 goto fail;
207 ret = krb5_ret_string(sp, &password);
208 if(ret){
209 krb5_free_principal(context->context, princ);
210 goto fail;
212 krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
213 krb5_warnx(context->context, "%s: %s %s", client, op, name);
214 #if 0
215 /* anyone can change her/his own password */
216 /* but not until there is a way to ensure that the
217 authentication was done via an initial ticket request */
218 if(!krb5_principal_compare(context->context, context->caller, princ))
219 ret = KADM5_AUTH_INSUFFICIENT;
220 if(ret)
221 #endif
222 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW);
223 if(ret){
224 krb5_free_principal(context->context, princ);
225 goto fail;
227 ret = kadm5_chpass_principal(kadm_handle, princ, password);
228 krb5_free_principal(context->context, princ);
229 memset(password, 0, strlen(password));
230 free(password);
231 krb5_storage_free(sp);
232 sp = krb5_storage_emem();
233 krb5_store_int32(sp, ret);
234 break;
236 case kadm_randkey:{
237 op = "RANDKEY";
238 ret = krb5_ret_principal(sp, &princ);
239 if(ret)
240 goto fail;
241 krb5_unparse_name_fixed(context->context, princ, name, sizeof(name));
242 krb5_warnx(context->context, "%s: %s %s", client, op, name);
243 #if 0
244 /* anyone can change her/his own password */
245 /* but not until there is a way to ensure that the
246 authentication was done via an initial ticket request */
247 if(!krb5_principal_compare(context->context, context->caller, princ))
248 ret = KADM5_AUTH_INSUFFICIENT;
249 if(ret)
250 #endif
251 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW);
252 if(ret){
253 krb5_free_principal(context->context, princ);
254 goto fail;
256 ret = kadm5_randkey_principal(kadm_handle, princ,
257 &new_keys, &n_keys);
258 krb5_free_principal(context->context, princ);
259 krb5_storage_free(sp);
260 sp = krb5_storage_emem();
261 krb5_store_int32(sp, ret);
262 if(ret == 0){
263 int i;
264 krb5_store_int32(sp, n_keys);
265 for(i = 0; i < n_keys; i++){
266 krb5_store_keyblock(sp, new_keys[i]);
267 krb5_free_keyblock_contents(context->context, &new_keys[i]);
270 break;
272 case kadm_get_privs:{
273 ret = kadm5_get_privs(kadm_handle, &mask);
274 krb5_storage_free(sp);
275 sp = krb5_storage_emem();
276 krb5_store_int32(sp, ret);
277 if(ret == 0)
278 krb5_store_int32(sp, mask);
279 break;
281 case kadm_get_princs:{
282 op = "LIST";
283 ret = krb5_ret_int32(sp, &tmp);
284 if(ret)
285 goto fail;
286 if(tmp){
287 ret = krb5_ret_string(sp, &exp);
288 if(ret)
289 goto fail;
290 }else
291 exp = NULL;
292 krb5_warnx(context->context, "%s: %s %s", client, op, exp ? exp : "*");
293 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_LIST);
294 if(ret){
295 free(exp);
296 goto fail;
298 ret = kadm5_get_principals(kadm_handle, exp, &princs, &n_princs);
299 free(exp);
300 krb5_storage_free(sp);
301 sp = krb5_storage_emem();
302 krb5_store_int32(sp, ret);
303 if(ret == 0){
304 int i;
305 krb5_store_int32(sp, n_princs);
306 for(i = 0; i < n_princs; i++)
307 krb5_store_string(sp, princs[i]);
308 kadm5_free_name_list(kadm_handle, princs, &n_princs);
310 break;
312 default:
313 krb5_warnx(context->context, "%s: UNKNOWN OP %d", client, cmd);
314 krb5_storage_free(sp);
315 sp = krb5_storage_emem();
316 krb5_store_int32(sp, KADM5_FAILURE);
317 break;
319 krb5_storage_to_data(sp, out);
320 krb5_storage_free(sp);
321 return 0;
322 fail:
323 krb5_warn(context->context, ret, "%s", op);
324 sp->seek(sp, 0, SEEK_SET);
325 krb5_store_int32(sp, ret);
326 krb5_storage_to_data(sp, out);
327 krb5_storage_free(sp);
328 return 0;
331 krb5_error_code
332 kadmind_loop(krb5_context context,
333 krb5_auth_context ac,
334 const char *client,
335 int fd)
337 krb5_error_code ret;
338 void *kadm_handle;
339 ret = kadm5_init_with_password_ctx(context,
340 client,
341 NULL,
342 KADM5_ADMIN_SERVICE,
343 NULL, 0, 0,
344 &kadm_handle);
345 if(ret) {
346 abort();
349 while(1){
350 krb5_data in, out, msg, reply;
351 unsigned char tmp[4];
352 unsigned long len;
353 ssize_t n;
354 struct iovec iov[2];
355 krb5_boolean krb4_packet = 0;
357 n = krb5_net_read(context, &fd, tmp, 4);
358 if(n == 0)
359 exit(0);
360 if(n < 0)
361 krb5_errx(context, 1, "read error: %d", errno);
362 if(n < 4)
363 krb5_errx(context, 1, "short read (%ld)", (long int)n);
364 k_get_int(tmp, &len, 4);
365 if(len > 0xffff && (len & 0xffff) == ('K' << 8) + 'A') {
366 len = len << 16;
367 krb4_packet = 1;
368 krb5_errx(context, 1, "packet appears to be version 4");
370 in.length = len;
371 in.data = malloc(in.length);
372 n = krb5_net_read(context, &fd, in.data, in.length);
373 if(n < 0)
374 krb5_errx(context, 1, "read error: %d", errno);
375 if(n < in.length)
376 krb5_errx(context, 1, "short read (%ld)", (long int)n);
377 if(!krb4_packet) {
378 ret = krb5_rd_priv(context, ac, &in, &out, NULL);
379 krb5_data_free(&in);
380 kadmind_dispatch(kadm_handle, &out, &msg);
381 krb5_data_free(&out);
383 ret = krb5_mk_priv(context, ac, &msg, &reply, NULL);
384 krb5_data_free(&msg);
385 if(ret)
386 krb5_err(context, 1, ret, "krb5_mk_priv");
388 k_put_int(tmp, reply.length, 4);
390 iov[0].iov_base = tmp;
391 iov[0].iov_len = 4;
392 iov[1].iov_base = reply.data;
393 iov[1].iov_len = reply.length;
394 n = writev(fd, iov, 2);
395 krb5_data_free(&reply);
396 if(n < 0)
397 krb5_err(context, 1, errno, "writev");
398 if(n < iov[0].iov_len + iov[1].iov_len)
399 krb5_errx(context, 1, "short write");