Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/kadmin/server.c
34865 views
1
/*
2
* Copyright (c) 1997 - 2005 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. 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.
20
*
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
31
* SUCH DAMAGE.
32
*/
33
34
#include "kadmin_locl.h"
35
#include <krb5-private.h>
36
37
static kadm5_ret_t
38
kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
39
krb5_data *in, krb5_data *out)
40
{
41
kadm5_ret_t ret = 0;
42
kadm5_ret_t ret_sp = 0;
43
int32_t cmd, mask, tmp;
44
kadm5_server_context *contextp = kadm_handlep;
45
char client[128], name[128], name2[128];
46
const char *op = "";
47
krb5_principal princ, princ2;
48
kadm5_principal_ent_rec ent;
49
char *password, *expression;
50
krb5_keyblock *new_keys;
51
int n_keys;
52
char **princs;
53
int n_princs;
54
krb5_storage *rsp = NULL; /* response goes here */
55
krb5_storage *sp = NULL;
56
57
memset(&ent, 0, sizeof(ent));
58
krb5_data_zero(out);
59
ret = krb5_unparse_name_fixed(contextp->context, contextp->caller,
60
client, sizeof(client));
61
62
sp = krb5_storage_from_data(in);
63
if (sp == NULL)
64
krb5_errx(contextp->context, 1, "out of memory");
65
66
ret = krb5_ret_int32(sp, &cmd);
67
if (ret) {
68
krb5_storage_free(sp);
69
goto fail;
70
}
71
switch(cmd){
72
case kadm_get:{
73
op = "GET";
74
ret = krb5_ret_principal(sp, &princ);
75
if(ret)
76
goto fail;
77
ret = krb5_ret_int32(sp, &mask);
78
if(ret){
79
krb5_free_principal(contextp->context, princ);
80
goto fail;
81
}
82
mask |= KADM5_PRINCIPAL;
83
ret = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
84
krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
85
if (ret == 0)
86
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
87
if(ret){
88
krb5_free_principal(contextp->context, princ);
89
goto fail;
90
}
91
ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask);
92
krb5_storage_free(sp);
93
sp = krb5_storage_emem();
94
krb5_store_int32(sp, ret);
95
if(ret == 0){
96
kadm5_store_principal_ent(sp, &ent);
97
kadm5_free_principal_ent(kadm_handlep, &ent);
98
}
99
krb5_free_principal(contextp->context, princ);
100
break;
101
}
102
case kadm_delete:{
103
op = "DELETE";
104
ret = krb5_ret_principal(sp, &princ);
105
if(ret)
106
goto fail;
107
ret = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
108
krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
109
if (ret == 0)
110
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
111
if(ret){
112
krb5_free_principal(contextp->context, princ);
113
goto fail;
114
}
115
ret = kadm5_delete_principal(kadm_handlep, princ);
116
krb5_free_principal(contextp->context, princ);
117
krb5_storage_free(sp);
118
sp = krb5_storage_emem();
119
krb5_store_int32(sp, ret);
120
break;
121
}
122
case kadm_create:{
123
op = "CREATE";
124
ret = kadm5_ret_principal_ent(sp, &ent);
125
if(ret)
126
goto fail;
127
ret = krb5_ret_int32(sp, &mask);
128
if(ret){
129
kadm5_free_principal_ent(contextp->context, &ent);
130
goto fail;
131
}
132
ret = krb5_ret_string(sp, &password);
133
if(ret){
134
kadm5_free_principal_ent(contextp->context, &ent);
135
goto fail;
136
}
137
ret = krb5_unparse_name_fixed(contextp->context, ent.principal,
138
name, sizeof(name));
139
krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
140
if (ret == 0)
141
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD,
142
ent.principal);
143
if(ret){
144
kadm5_free_principal_ent(contextp->context, &ent);
145
memset(password, 0, strlen(password));
146
free(password);
147
goto fail;
148
}
149
ret = kadm5_create_principal(kadm_handlep, &ent,
150
mask, password);
151
kadm5_free_principal_ent(kadm_handlep, &ent);
152
memset(password, 0, strlen(password));
153
free(password);
154
krb5_storage_free(sp);
155
sp = krb5_storage_emem();
156
krb5_store_int32(sp, ret);
157
break;
158
}
159
case kadm_modify:{
160
op = "MODIFY";
161
ret = kadm5_ret_principal_ent(sp, &ent);
162
if(ret)
163
goto fail;
164
ret = krb5_ret_int32(sp, &mask);
165
if(ret){
166
kadm5_free_principal_ent(contextp, &ent);
167
goto fail;
168
}
169
ret = krb5_unparse_name_fixed(contextp->context, ent.principal,
170
name, sizeof(name));
171
krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
172
if (ret == 0)
173
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_MODIFY,
174
ent.principal);
175
if(ret){
176
kadm5_free_principal_ent(contextp, &ent);
177
goto fail;
178
}
179
ret = kadm5_modify_principal(kadm_handlep, &ent, mask);
180
kadm5_free_principal_ent(kadm_handlep, &ent);
181
krb5_storage_free(sp);
182
sp = krb5_storage_emem();
183
krb5_store_int32(sp, ret);
184
break;
185
}
186
case kadm_rename:{
187
op = "RENAME";
188
ret = krb5_ret_principal(sp, &princ);
189
if(ret)
190
goto fail;
191
ret = krb5_ret_principal(sp, &princ2);
192
if(ret){
193
krb5_free_principal(contextp->context, princ);
194
goto fail;
195
}
196
ret = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
197
if (ret == 0)
198
ret = krb5_unparse_name_fixed(contextp->context, princ2, name2, sizeof(name2));
199
krb5_warnx(contextp->context, "%s: %s %s -> %s",
200
client, op, name, name2);
201
if (ret == 0)
202
ret = _kadm5_acl_check_permission(contextp,
203
KADM5_PRIV_ADD,
204
princ2)
205
|| _kadm5_acl_check_permission(contextp,
206
KADM5_PRIV_DELETE,
207
princ);
208
if(ret){
209
krb5_free_principal(contextp->context, princ);
210
krb5_free_principal(contextp->context, princ2);
211
goto fail;
212
}
213
ret = kadm5_rename_principal(kadm_handlep, princ, princ2);
214
krb5_free_principal(contextp->context, princ);
215
krb5_free_principal(contextp->context, princ2);
216
krb5_storage_free(sp);
217
sp = krb5_storage_emem();
218
krb5_store_int32(sp, ret);
219
break;
220
}
221
case kadm_chpass:{
222
op = "CHPASS";
223
ret = krb5_ret_principal(sp, &princ);
224
if(ret)
225
goto fail;
226
ret = krb5_ret_string(sp, &password);
227
if(ret){
228
krb5_free_principal(contextp->context, princ);
229
goto fail;
230
}
231
ret = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
232
krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
233
234
/*
235
* The change is allowed if at least one of:
236
*
237
* a) allowed by sysadmin
238
* b) it's for the principal him/herself and this was an
239
* initial ticket, but then, check with the password quality
240
* function.
241
* c) the user is on the CPW ACL.
242
*/
243
244
if (ret == 0) {
245
if (krb5_config_get_bool_default(contextp->context, NULL, TRUE,
246
"kadmin", "allow_self_change_password", NULL)
247
&& initial
248
&& krb5_principal_compare (contextp->context, contextp->caller,
249
princ))
250
{
251
krb5_data pwd_data;
252
const char *pwd_reason;
253
254
pwd_data.data = password;
255
pwd_data.length = strlen(password);
256
257
pwd_reason = kadm5_check_password_quality (contextp->context,
258
princ, &pwd_data);
259
if (pwd_reason != NULL)
260
ret = KADM5_PASS_Q_DICT;
261
else
262
ret = 0;
263
} else
264
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
265
}
266
267
if(ret) {
268
krb5_free_principal(contextp->context, princ);
269
memset(password, 0, strlen(password));
270
free(password);
271
goto fail;
272
}
273
ret = kadm5_chpass_principal(kadm_handlep, princ, password);
274
krb5_free_principal(contextp->context, princ);
275
memset(password, 0, strlen(password));
276
free(password);
277
krb5_storage_free(sp);
278
sp = krb5_storage_emem();
279
krb5_store_int32(sp, ret);
280
break;
281
}
282
case kadm_chpass_with_key:{
283
int i;
284
krb5_key_data *key_data;
285
int n_key_data;
286
287
op = "CHPASS_WITH_KEY";
288
ret = krb5_ret_principal(sp, &princ);
289
if(ret)
290
goto fail;
291
ret = krb5_ret_int32(sp, &n_key_data);
292
if (ret) {
293
krb5_free_principal(contextp->context, princ);
294
goto fail;
295
}
296
/* n_key_data will be squeezed into an int16_t below. */
297
if (n_key_data < 0 || n_key_data >= 1 << 16 ||
298
(size_t)n_key_data > UINT_MAX/sizeof(*key_data)) {
299
ret = ERANGE;
300
krb5_free_principal(contextp->context, princ);
301
goto fail;
302
}
303
304
key_data = malloc (n_key_data * sizeof(*key_data));
305
if (key_data == NULL && n_key_data != 0) {
306
ret = ENOMEM;
307
krb5_free_principal(contextp->context, princ);
308
goto fail;
309
}
310
311
for (i = 0; i < n_key_data; ++i) {
312
ret = kadm5_ret_key_data (sp, &key_data[i]);
313
if (ret) {
314
int16_t dummy = i;
315
316
kadm5_free_key_data (contextp, &dummy, key_data);
317
free (key_data);
318
krb5_free_principal(contextp->context, princ);
319
goto fail;
320
}
321
}
322
323
ret = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
324
krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
325
326
/*
327
* The change is only allowed if the user is on the CPW ACL,
328
* this it to force password quality check on the user.
329
*/
330
331
if (ret == 0)
332
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
333
if(ret) {
334
int16_t dummy = n_key_data;
335
336
kadm5_free_key_data (contextp, &dummy, key_data);
337
free (key_data);
338
krb5_free_principal(contextp->context, princ);
339
goto fail;
340
}
341
ret = kadm5_chpass_principal_with_key(kadm_handlep, princ,
342
n_key_data, key_data);
343
{
344
int16_t dummy = n_key_data;
345
kadm5_free_key_data (contextp, &dummy, key_data);
346
}
347
free (key_data);
348
krb5_free_principal(contextp->context, princ);
349
krb5_storage_free(sp);
350
sp = krb5_storage_emem();
351
krb5_store_int32(sp, ret);
352
break;
353
}
354
case kadm_randkey:{
355
op = "RANDKEY";
356
ret = krb5_ret_principal(sp, &princ);
357
if(ret)
358
goto fail;
359
ret = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
360
krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
361
/*
362
* The change is allowed if at least one of:
363
* a) it's for the principal him/herself and this was an initial ticket
364
* b) the user is on the CPW ACL.
365
*/
366
367
if (ret == 0) {
368
if (initial
369
&& krb5_principal_compare (contextp->context, contextp->caller,
370
princ))
371
ret = 0;
372
else
373
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
374
}
375
if(ret) {
376
krb5_free_principal(contextp->context, princ);
377
goto fail;
378
}
379
ret = kadm5_randkey_principal(kadm_handlep, princ,
380
&new_keys, &n_keys);
381
krb5_free_principal(contextp->context, princ);
382
krb5_storage_free(sp);
383
sp = krb5_storage_emem();
384
krb5_store_int32(sp, ret);
385
if(ret == 0){
386
int i;
387
krb5_store_int32(sp, n_keys);
388
for(i = 0; i < n_keys; i++){
389
krb5_store_keyblock(sp, new_keys[i]);
390
krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
391
}
392
free(new_keys);
393
}
394
break;
395
}
396
case kadm_get_privs:{
397
uint32_t privs;
398
ret = kadm5_get_privs(kadm_handlep, &privs);
399
krb5_storage_free(sp);
400
sp = krb5_storage_emem();
401
krb5_store_int32(sp, ret);
402
if(ret == 0)
403
krb5_store_uint32(sp, privs);
404
break;
405
}
406
case kadm_get_princs:{
407
op = "LIST";
408
ret = krb5_ret_int32(sp, &tmp);
409
if(ret)
410
goto fail;
411
if(tmp){
412
ret = krb5_ret_string(sp, &expression);
413
if(ret)
414
goto fail;
415
}else
416
expression = NULL;
417
krb5_warnx(contextp->context, "%s: %s %s", client, op,
418
expression ? expression : "*");
419
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_LIST, NULL);
420
if(ret){
421
free(expression);
422
goto fail;
423
}
424
ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs);
425
free(expression);
426
krb5_storage_free(sp);
427
sp = krb5_storage_emem();
428
krb5_store_int32(sp, ret);
429
if(ret == 0){
430
int i;
431
if ((ret = krb5_store_int32(sp, n_princs)))
432
goto fail;
433
for(i = 0; i < n_princs; i++)
434
if ((ret = krb5_store_string(sp, princs[i])))
435
goto fail;
436
kadm5_free_name_list(kadm_handlep, princs, &n_princs);
437
}
438
break;
439
}
440
default:
441
krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd);
442
krb5_storage_free(sp);
443
sp = krb5_storage_emem();
444
krb5_store_int32(sp, KADM5_FAILURE);
445
break;
446
}
447
krb5_storage_to_data(sp, out);
448
krb5_storage_free(sp);
449
return 0;
450
fail:
451
krb5_warn(contextp->context, ret, "%s", op);
452
krb5_storage_seek(sp, 0, SEEK_SET);
453
krb5_store_int32(sp, ret);
454
krb5_storage_to_data(sp, out);
455
krb5_storage_free(sp);
456
return ret;
457
}
458
459
static void
460
v5_loop (krb5_context contextp,
461
krb5_auth_context ac,
462
krb5_boolean initial,
463
void *kadm_handlep,
464
krb5_socket_t fd)
465
{
466
krb5_error_code ret;
467
krb5_data in, out;
468
469
for (;;) {
470
doing_useful_work = 0;
471
if(term_flag)
472
exit(0);
473
ret = krb5_read_priv_message(contextp, ac, &fd, &in);
474
if(ret == HEIM_ERR_EOF)
475
exit(0);
476
if (in.length == 0)
477
ret = HEIM_ERR_OPNOTSUPP;
478
if(ret)
479
krb5_err(contextp, 1, ret, "krb5_read_priv_message");
480
doing_useful_work = 1;
481
kadmind_dispatch(kadm_handlep, initial, &in, &out);
482
krb5_data_free(&in);
483
ret = krb5_write_priv_message(contextp, ac, &fd, &out);
484
if(ret)
485
krb5_err(contextp, 1, ret, "krb5_write_priv_message");
486
}
487
}
488
489
static krb5_boolean
490
match_appl_version(const void *data, const char *appl_version)
491
{
492
unsigned minor;
493
if(sscanf(appl_version, "KADM0.%u", &minor) != 1)
494
return 0;
495
/*XXX*/
496
*(unsigned*)(intptr_t)data = minor;
497
return 1;
498
}
499
500
static void
501
handle_v5(krb5_context contextp,
502
krb5_keytab keytab,
503
krb5_socket_t fd)
504
{
505
krb5_error_code ret;
506
krb5_ticket *ticket;
507
char *server_name;
508
char *client;
509
void *kadm_handlep;
510
krb5_boolean initial;
511
krb5_auth_context ac = NULL;
512
513
unsigned kadm_version;
514
kadm5_config_params realm_params;
515
516
ret = krb5_recvauth_match_version(contextp, &ac, &fd,
517
match_appl_version, &kadm_version,
518
NULL, KRB5_RECVAUTH_IGNORE_VERSION,
519
keytab, &ticket);
520
if (ret)
521
krb5_err(contextp, 1, ret, "krb5_recvauth");
522
523
ret = krb5_unparse_name (contextp, ticket->server, &server_name);
524
if (ret)
525
krb5_err (contextp, 1, ret, "krb5_unparse_name");
526
527
if (strncmp (server_name, KADM5_ADMIN_SERVICE,
528
strlen(KADM5_ADMIN_SERVICE)) != 0)
529
krb5_errx (contextp, 1, "ticket for strange principal (%s)",
530
server_name);
531
532
free (server_name);
533
534
memset(&realm_params, 0, sizeof(realm_params));
535
536
if(kadm_version == 1) {
537
krb5_data params;
538
ret = krb5_read_priv_message(contextp, ac, &fd, &params);
539
if(ret)
540
krb5_err(contextp, 1, ret, "krb5_read_priv_message");
541
ret = _kadm5_unmarshal_params(contextp, &params, &realm_params);
542
if(ret)
543
krb5_err(contextp, 1, ret, "Could not read or parse kadm5 parameters");
544
}
545
546
initial = ticket->ticket.flags.initial;
547
ret = krb5_unparse_name(contextp, ticket->client, &client);
548
if (ret)
549
krb5_err (contextp, 1, ret, "krb5_unparse_name");
550
krb5_free_ticket (contextp, ticket);
551
ret = kadm5_s_init_with_password_ctx(contextp,
552
client,
553
NULL,
554
KADM5_ADMIN_SERVICE,
555
&realm_params,
556
0, 0,
557
&kadm_handlep);
558
if(ret)
559
krb5_err (contextp, 1, ret, "kadm5_init_with_password_ctx");
560
v5_loop (contextp, ac, initial, kadm_handlep, fd);
561
}
562
563
krb5_error_code
564
kadmind_loop(krb5_context contextp,
565
krb5_keytab keytab,
566
krb5_socket_t sock)
567
{
568
u_char buf[sizeof(KRB5_SENDAUTH_VERSION) + 4];
569
ssize_t n;
570
unsigned long len;
571
572
n = krb5_net_read(contextp, &sock, buf, 4);
573
if(n == 0)
574
exit(0);
575
if(n < 0)
576
krb5_err(contextp, 1, errno, "read");
577
_krb5_get_int(buf, &len, 4);
578
579
if (len == sizeof(KRB5_SENDAUTH_VERSION)) {
580
581
n = krb5_net_read(contextp, &sock, buf + 4, len);
582
if (n < 0)
583
krb5_err (contextp, 1, errno, "reading sendauth version");
584
if (n == 0)
585
krb5_errx (contextp, 1, "EOF reading sendauth version");
586
587
if(memcmp(buf + 4, KRB5_SENDAUTH_VERSION, len) == 0) {
588
handle_v5(contextp, keytab, sock);
589
return 0;
590
}
591
len += 4;
592
} else
593
len = 4;
594
595
handle_mit(contextp, buf, len, sock);
596
597
return 0;
598
}
599
600