Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/kadmin/rpc.c
34865 views
1
/*
2
* Copyright (c) 2008 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
36
#include <gssapi/gssapi.h>
37
//#include <gssapi_krb5.h>
38
//#include <gssapi_spnego.h>
39
40
static gss_OID_desc krb5_mechanism =
41
{9, (void *)(uintptr_t) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
42
#define GSS_KRB5_MECHANISM (&krb5_mechanism)
43
44
#define CHECK(x) \
45
do { \
46
int __r; \
47
if ((__r = (x))) { \
48
krb5_errx(dcontext, 1, "Failed (%d) on %s:%d", \
49
__r, __FILE__, __LINE__); \
50
} \
51
} while(0)
52
53
static krb5_context dcontext;
54
55
#define INSIST(x) CHECK(!(x))
56
57
#define VERSION2 0x12345702
58
59
#define LAST_FRAGMENT 0x80000000
60
61
#define RPC_VERSION 2
62
#define KADM_SERVER 2112
63
#define VVERSION 2
64
#define FLAVOR_GSS 6
65
#define FLAVOR_GSS_VERSION 1
66
67
struct opaque_auth {
68
uint32_t flavor;
69
krb5_data data;
70
};
71
72
struct call_header {
73
uint32_t xid;
74
uint32_t rpcvers;
75
uint32_t prog;
76
uint32_t vers;
77
uint32_t proc;
78
struct opaque_auth cred;
79
struct opaque_auth verf;
80
};
81
82
enum {
83
RPG_DATA = 0,
84
RPG_INIT = 1,
85
RPG_CONTINUE_INIT = 2,
86
RPG_DESTROY = 3
87
};
88
89
enum {
90
rpg_privacy = 3
91
};
92
93
/*
94
struct chrand_ret {
95
krb5_ui_4 api_version;
96
kadm5_ret_t ret;
97
int n_keys;
98
krb5_keyblock *keys;
99
};
100
*/
101
102
103
struct gcred {
104
uint32_t version;
105
uint32_t proc;
106
uint32_t seq_num;
107
uint32_t service;
108
krb5_data handle;
109
};
110
111
static int
112
parse_name(const unsigned char *p, size_t len,
113
const gss_OID oid, char **name)
114
{
115
size_t l;
116
117
if (len < 4)
118
return 1;
119
120
/* TOK_ID */
121
if (memcmp(p, "\x04\x01", 2) != 0)
122
return 1;
123
len -= 2;
124
p += 2;
125
126
/* MECH_LEN */
127
l = (p[0] << 8) | p[1];
128
len -= 2;
129
p += 2;
130
if (l < 2 || len < l)
131
return 1;
132
133
/* oid wrapping */
134
if (p[0] != 6 || p[1] != l - 2)
135
return 1;
136
p += 2;
137
l -= 2;
138
len -= 2;
139
140
/* MECH */
141
if (l != oid->length || memcmp(p, oid->elements, oid->length) != 0)
142
return 1;
143
len -= l;
144
p += l;
145
146
/* MECHNAME_LEN */
147
if (len < 4)
148
return 1;
149
l = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
150
len -= 4;
151
p += 4;
152
153
/* MECH NAME */
154
if (len != l)
155
return 1;
156
157
*name = malloc(l + 1);
158
INSIST(*name != NULL);
159
memcpy(*name, p, l);
160
(*name)[l] = '\0';
161
162
return 0;
163
}
164
165
166
167
static void
168
gss_error(krb5_context contextp,
169
gss_OID mech, OM_uint32 type, OM_uint32 error)
170
{
171
OM_uint32 new_stat;
172
OM_uint32 msg_ctx = 0;
173
gss_buffer_desc status_string;
174
OM_uint32 ret;
175
176
do {
177
ret = gss_display_status (&new_stat,
178
error,
179
type,
180
mech,
181
&msg_ctx,
182
&status_string);
183
krb5_warnx(contextp, "%.*s",
184
(int)status_string.length,
185
(char *)status_string.value);
186
gss_release_buffer (&new_stat, &status_string);
187
} while (!GSS_ERROR(ret) && msg_ctx != 0);
188
}
189
190
static void
191
gss_print_errors (krb5_context contextp,
192
OM_uint32 maj_stat, OM_uint32 min_stat)
193
{
194
gss_error(contextp, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
195
gss_error(contextp, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat);
196
}
197
198
static int
199
read_data(krb5_storage *sp, krb5_storage *msg, size_t len)
200
{
201
char buf[1024];
202
203
while (len) {
204
size_t tlen = len;
205
ssize_t slen;
206
207
if (tlen > sizeof(buf))
208
tlen = sizeof(buf);
209
210
slen = krb5_storage_read(sp, buf, tlen);
211
INSIST((size_t)slen == tlen);
212
213
slen = krb5_storage_write(msg, buf, tlen);
214
INSIST((size_t)slen == tlen);
215
216
len -= tlen;
217
}
218
return 0;
219
}
220
221
static int
222
collect_framents(krb5_storage *sp, krb5_storage *msg)
223
{
224
krb5_error_code ret;
225
uint32_t len;
226
int last_fragment;
227
size_t total_len = 0;
228
229
do {
230
ret = krb5_ret_uint32(sp, &len);
231
if (ret)
232
return ret;
233
234
last_fragment = (len & LAST_FRAGMENT);
235
len &= ~LAST_FRAGMENT;
236
237
CHECK(read_data(sp, msg, len));
238
total_len += len;
239
240
} while(!last_fragment || total_len == 0);
241
242
return 0;
243
}
244
245
static krb5_error_code
246
store_data_xdr(krb5_storage *sp, krb5_data data)
247
{
248
krb5_error_code ret;
249
size_t res;
250
251
ret = krb5_store_data(sp, data);
252
if (ret)
253
return ret;
254
res = 4 - (data.length % 4);
255
if (res != 4) {
256
static const char zero[4] = { 0, 0, 0, 0 };
257
258
ret = krb5_storage_write(sp, zero, res);
259
if((size_t)ret != res)
260
return (ret < 0)? errno : krb5_storage_get_eof_code(sp);
261
}
262
return 0;
263
}
264
265
static krb5_error_code
266
ret_data_xdr(krb5_storage *sp, krb5_data *data)
267
{
268
krb5_error_code ret;
269
ret = krb5_ret_data(sp, data);
270
if (ret)
271
return ret;
272
273
if ((data->length % 4) != 0) {
274
char buf[4];
275
size_t res;
276
277
res = 4 - (data->length % 4);
278
if (res != 4) {
279
ret = krb5_storage_read(sp, buf, res);
280
if((size_t)ret != res)
281
return (ret < 0)? errno : krb5_storage_get_eof_code(sp);
282
}
283
}
284
return 0;
285
}
286
287
static krb5_error_code
288
ret_auth_opaque(krb5_storage *msg, struct opaque_auth *ao)
289
{
290
krb5_error_code ret;
291
ret = krb5_ret_uint32(msg, &ao->flavor);
292
if (ret) return ret;
293
ret = ret_data_xdr(msg, &ao->data);
294
return ret;
295
}
296
297
static int
298
ret_gcred(krb5_data *data, struct gcred *gcred)
299
{
300
krb5_storage *sp;
301
302
memset(gcred, 0, sizeof(*gcred));
303
304
sp = krb5_storage_from_data(data);
305
INSIST(sp != NULL);
306
307
CHECK(krb5_ret_uint32(sp, &gcred->version));
308
CHECK(krb5_ret_uint32(sp, &gcred->proc));
309
CHECK(krb5_ret_uint32(sp, &gcred->seq_num));
310
CHECK(krb5_ret_uint32(sp, &gcred->service));
311
CHECK(ret_data_xdr(sp, &gcred->handle));
312
313
krb5_storage_free(sp);
314
315
return 0;
316
}
317
318
static krb5_error_code
319
store_gss_init_res(krb5_storage *sp, krb5_data handle,
320
OM_uint32 maj_stat, OM_uint32 min_stat,
321
uint32_t seq_window, gss_buffer_t gout)
322
{
323
krb5_error_code ret;
324
krb5_data out;
325
326
out.data = gout->value;
327
out.length = gout->length;
328
329
ret = store_data_xdr(sp, handle);
330
if (ret) return ret;
331
ret = krb5_store_uint32(sp, maj_stat);
332
if (ret) return ret;
333
ret = krb5_store_uint32(sp, min_stat);
334
if (ret) return ret;
335
ret = store_data_xdr(sp, out);
336
return ret;
337
}
338
339
static int
340
store_string_xdr(krb5_storage *sp, const char *str)
341
{
342
krb5_data c;
343
if (str) {
344
c.data = rk_UNCONST(str);
345
c.length = strlen(str) + 1;
346
} else
347
krb5_data_zero(&c);
348
349
return store_data_xdr(sp, c);
350
}
351
352
static int
353
ret_string_xdr(krb5_storage *sp, char **str)
354
{
355
krb5_data c;
356
*str = NULL;
357
CHECK(ret_data_xdr(sp, &c));
358
if (c.length) {
359
*str = malloc(c.length + 1);
360
INSIST(*str != NULL);
361
memcpy(*str, c.data, c.length);
362
(*str)[c.length] = '\0';
363
}
364
krb5_data_free(&c);
365
return 0;
366
}
367
368
static int
369
store_principal_xdr(krb5_context contextp,
370
krb5_storage *sp,
371
krb5_principal p)
372
{
373
char *str;
374
CHECK(krb5_unparse_name(contextp, p, &str));
375
CHECK(store_string_xdr(sp, str));
376
free(str);
377
return 0;
378
}
379
380
static int
381
ret_principal_xdr(krb5_context contextp,
382
krb5_storage *sp,
383
krb5_principal *p)
384
{
385
char *str;
386
*p = NULL;
387
CHECK(ret_string_xdr(sp, &str));
388
if (str) {
389
CHECK(krb5_parse_name(contextp, str, p));
390
free(str);
391
}
392
return 0;
393
}
394
395
static int
396
store_principal_ent(krb5_context contextp,
397
krb5_storage *sp,
398
kadm5_principal_ent_rec *ent)
399
{
400
int i;
401
402
CHECK(store_principal_xdr(contextp, sp, ent->principal));
403
CHECK(krb5_store_uint32(sp, ent->princ_expire_time));
404
CHECK(krb5_store_uint32(sp, ent->pw_expiration));
405
CHECK(krb5_store_uint32(sp, ent->last_pwd_change));
406
CHECK(krb5_store_uint32(sp, ent->max_life));
407
CHECK(krb5_store_int32(sp, ent->mod_name == NULL));
408
if (ent->mod_name)
409
CHECK(store_principal_xdr(contextp, sp, ent->mod_name));
410
CHECK(krb5_store_uint32(sp, ent->mod_date));
411
CHECK(krb5_store_uint32(sp, ent->attributes));
412
CHECK(krb5_store_uint32(sp, ent->kvno));
413
CHECK(krb5_store_uint32(sp, ent->mkvno));
414
CHECK(store_string_xdr(sp, ent->policy));
415
CHECK(krb5_store_int32(sp, ent->aux_attributes));
416
CHECK(krb5_store_int32(sp, ent->max_renewable_life));
417
CHECK(krb5_store_int32(sp, ent->last_success));
418
CHECK(krb5_store_int32(sp, ent->last_failed));
419
CHECK(krb5_store_int32(sp, ent->fail_auth_count));
420
CHECK(krb5_store_int32(sp, ent->n_key_data));
421
CHECK(krb5_store_int32(sp, ent->n_tl_data));
422
CHECK(krb5_store_int32(sp, ent->n_tl_data == 0));
423
if (ent->n_tl_data) {
424
krb5_tl_data *tp;
425
426
for (tp = ent->tl_data; tp; tp = tp->tl_data_next) {
427
krb5_data c;
428
c.length = tp->tl_data_length;
429
c.data = tp->tl_data_contents;
430
431
CHECK(krb5_store_int32(sp, 0)); /* last item */
432
CHECK(krb5_store_int32(sp, tp->tl_data_type));
433
CHECK(store_data_xdr(sp, c));
434
}
435
CHECK(krb5_store_int32(sp, 1)); /* last item */
436
}
437
438
CHECK(krb5_store_int32(sp, ent->n_key_data));
439
for (i = 0; i < ent->n_key_data; i++) {
440
CHECK(krb5_store_uint32(sp, 2));
441
CHECK(krb5_store_uint32(sp, ent->kvno));
442
CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[0]));
443
CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[1]));
444
}
445
446
return 0;
447
}
448
449
static int
450
ret_principal_ent(krb5_context contextp,
451
krb5_storage *sp,
452
kadm5_principal_ent_rec *ent)
453
{
454
uint32_t flag, num;
455
size_t i;
456
457
memset(ent, 0, sizeof(*ent));
458
459
CHECK(ret_principal_xdr(contextp, sp, &ent->principal));
460
CHECK(krb5_ret_uint32(sp, &flag));
461
ent->princ_expire_time = flag;
462
CHECK(krb5_ret_uint32(sp, &flag));
463
ent->pw_expiration = flag;
464
CHECK(krb5_ret_uint32(sp, &flag));
465
ent->last_pwd_change = flag;
466
CHECK(krb5_ret_uint32(sp, &flag));
467
ent->max_life = flag;
468
CHECK(krb5_ret_uint32(sp, &flag));
469
if (flag == 0)
470
ret_principal_xdr(contextp, sp, &ent->mod_name);
471
CHECK(krb5_ret_uint32(sp, &flag));
472
ent->mod_date = flag;
473
CHECK(krb5_ret_uint32(sp, &flag));
474
ent->attributes = flag;
475
CHECK(krb5_ret_uint32(sp, &flag));
476
ent->kvno = flag;
477
CHECK(krb5_ret_uint32(sp, &flag));
478
ent->mkvno = flag;
479
CHECK(ret_string_xdr(sp, &ent->policy));
480
CHECK(krb5_ret_uint32(sp, &flag));
481
ent->aux_attributes = flag;
482
CHECK(krb5_ret_uint32(sp, &flag));
483
ent->max_renewable_life = flag;
484
CHECK(krb5_ret_uint32(sp, &flag));
485
ent->last_success = flag;
486
CHECK(krb5_ret_uint32(sp, &flag));
487
ent->last_failed = flag;
488
CHECK(krb5_ret_uint32(sp, &flag));
489
ent->fail_auth_count = flag;
490
CHECK(krb5_ret_uint32(sp, &flag));
491
ent->n_key_data = flag;
492
CHECK(krb5_ret_uint32(sp, &flag));
493
ent->n_tl_data = flag;
494
CHECK(krb5_ret_uint32(sp, &flag));
495
if (flag == 0) {
496
krb5_tl_data **tp = &ent->tl_data;
497
size_t count = 0;
498
499
while(1) {
500
krb5_data c;
501
CHECK(krb5_ret_uint32(sp, &flag)); /* last item */
502
if (flag)
503
break;
504
*tp = calloc(1, sizeof(**tp));
505
INSIST(*tp != NULL);
506
CHECK(krb5_ret_uint32(sp, &flag));
507
(*tp)->tl_data_type = flag;
508
CHECK(ret_data_xdr(sp, &c));
509
(*tp)->tl_data_length = c.length;
510
(*tp)->tl_data_contents = c.data;
511
tp = &(*tp)->tl_data_next;
512
513
count++;
514
}
515
INSIST((size_t)ent->n_tl_data == count);
516
} else {
517
INSIST(ent->n_tl_data == 0);
518
}
519
520
CHECK(krb5_ret_uint32(sp, &num));
521
INSIST(num == (uint32_t)ent->n_key_data);
522
523
ent->key_data = calloc(num, sizeof(ent->key_data[0]));
524
INSIST(ent->key_data != NULL);
525
526
for (i = 0; i < num; i++) {
527
CHECK(krb5_ret_uint32(sp, &flag)); /* data version */
528
INSIST(flag > 1);
529
CHECK(krb5_ret_uint32(sp, &flag));
530
ent->kvno = flag;
531
CHECK(krb5_ret_uint32(sp, &flag));
532
ent->key_data[i].key_data_type[0] = flag;
533
CHECK(krb5_ret_uint32(sp, &flag));
534
ent->key_data[i].key_data_type[1] = flag;
535
}
536
537
return 0;
538
}
539
540
/*
541
*
542
*/
543
544
static void
545
proc_create_principal(kadm5_server_context *contextp,
546
krb5_storage *in,
547
krb5_storage *out)
548
{
549
uint32_t version, mask;
550
kadm5_principal_ent_rec ent;
551
krb5_error_code ret;
552
char *password;
553
554
memset(&ent, 0, sizeof(ent));
555
556
CHECK(krb5_ret_uint32(in, &version));
557
INSIST(version == VERSION2);
558
CHECK(ret_principal_ent(contextp->context, in, &ent));
559
CHECK(krb5_ret_uint32(in, &mask));
560
CHECK(ret_string_xdr(in, &password));
561
562
INSIST(ent.principal);
563
564
565
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, ent.principal);
566
if (ret)
567
goto fail;
568
569
ret = kadm5_create_principal(contextp, &ent, mask, password);
570
571
fail:
572
krb5_warn(contextp->context, ret, "create principal");
573
CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
574
CHECK(krb5_store_uint32(out, ret)); /* code */
575
576
free(password);
577
kadm5_free_principal_ent(contextp, &ent);
578
}
579
580
static void
581
proc_delete_principal(kadm5_server_context *contextp,
582
krb5_storage *in,
583
krb5_storage *out)
584
{
585
uint32_t version;
586
krb5_principal princ;
587
krb5_error_code ret;
588
589
CHECK(krb5_ret_uint32(in, &version));
590
INSIST(version == VERSION2);
591
CHECK(ret_principal_xdr(contextp->context, in, &princ));
592
593
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
594
if (ret)
595
goto fail;
596
597
ret = kadm5_delete_principal(contextp, princ);
598
599
fail:
600
krb5_warn(contextp->context, ret, "delete principal");
601
CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
602
CHECK(krb5_store_uint32(out, ret)); /* code */
603
604
krb5_free_principal(contextp->context, princ);
605
}
606
607
static void
608
proc_get_principal(kadm5_server_context *contextp,
609
krb5_storage *in,
610
krb5_storage *out)
611
{
612
uint32_t version, mask;
613
krb5_principal princ;
614
kadm5_principal_ent_rec ent;
615
krb5_error_code ret;
616
617
memset(&ent, 0, sizeof(ent));
618
619
CHECK(krb5_ret_uint32(in, &version));
620
INSIST(version == VERSION2);
621
CHECK(ret_principal_xdr(contextp->context, in, &princ));
622
CHECK(krb5_ret_uint32(in, &mask));
623
624
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
625
if(ret)
626
goto fail;
627
628
ret = kadm5_get_principal(contextp, princ, &ent, mask);
629
630
fail:
631
krb5_warn(contextp->context, ret, "get principal principal");
632
633
CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
634
CHECK(krb5_store_uint32(out, ret)); /* code */
635
if (ret == 0) {
636
CHECK(store_principal_ent(contextp->context, out, &ent));
637
}
638
krb5_free_principal(contextp->context, princ);
639
kadm5_free_principal_ent(contextp, &ent);
640
}
641
642
static void
643
proc_chrand_principal_v2(kadm5_server_context *contextp,
644
krb5_storage *in,
645
krb5_storage *out)
646
{
647
krb5_error_code ret;
648
krb5_principal princ;
649
uint32_t version;
650
krb5_keyblock *new_keys;
651
int n_keys;
652
653
CHECK(krb5_ret_uint32(in, &version));
654
INSIST(version == VERSION2);
655
CHECK(ret_principal_xdr(contextp->context, in, &princ));
656
657
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
658
if(ret)
659
goto fail;
660
661
ret = kadm5_randkey_principal(contextp, princ,
662
&new_keys, &n_keys);
663
664
fail:
665
krb5_warn(contextp->context, ret, "rand key principal");
666
667
CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
668
CHECK(krb5_store_uint32(out, ret));
669
if (ret == 0) {
670
int i;
671
CHECK(krb5_store_int32(out, n_keys));
672
673
for(i = 0; i < n_keys; i++){
674
CHECK(krb5_store_uint32(out, new_keys[i].keytype));
675
CHECK(store_data_xdr(out, new_keys[i].keyvalue));
676
krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
677
}
678
free(new_keys);
679
}
680
krb5_free_principal(contextp->context, princ);
681
}
682
683
static void
684
proc_init(kadm5_server_context *contextp,
685
krb5_storage *in,
686
krb5_storage *out)
687
{
688
CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
689
CHECK(krb5_store_uint32(out, 0)); /* code */
690
CHECK(krb5_store_uint32(out, 0)); /* code */
691
}
692
693
struct krb5_proc {
694
const char *name;
695
void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *);
696
} procs[] = {
697
{ "NULL", NULL },
698
{ "create principal", proc_create_principal },
699
{ "delete principal", proc_delete_principal },
700
{ "modify principal", NULL },
701
{ "rename principal", NULL },
702
{ "get principal", proc_get_principal },
703
{ "chpass principal", NULL },
704
{ "chrand principal", proc_chrand_principal_v2 },
705
{ "create policy", NULL },
706
{ "delete policy", NULL },
707
{ "modify policy", NULL },
708
{ "get policy", NULL },
709
{ "get privs", NULL },
710
{ "init", proc_init },
711
{ "get principals", NULL },
712
{ "get polices", NULL },
713
{ "setkey principal", NULL },
714
{ "setkey principal v4", NULL },
715
{ "create principal v3", NULL },
716
{ "chpass principal v3", NULL },
717
{ "chrand principal v3", NULL },
718
{ "setkey principal v3", NULL }
719
};
720
721
static krb5_error_code
722
copyheader(krb5_storage *sp, krb5_data *data)
723
{
724
off_t off;
725
ssize_t sret;
726
727
off = krb5_storage_seek(sp, 0, SEEK_CUR);
728
729
CHECK(krb5_data_alloc(data, off));
730
INSIST((size_t)off == data->length);
731
krb5_storage_seek(sp, 0, SEEK_SET);
732
sret = krb5_storage_read(sp, data->data, data->length);
733
INSIST(sret == off);
734
INSIST(off == krb5_storage_seek(sp, 0, SEEK_CUR));
735
736
return 0;
737
}
738
739
struct gctx {
740
krb5_data handle;
741
gss_ctx_id_t ctx;
742
uint32_t seq_num;
743
int done;
744
int inprogress;
745
};
746
747
static int
748
process_stream(krb5_context contextp,
749
unsigned char *buf, size_t ilen,
750
krb5_storage *sp)
751
{
752
krb5_error_code ret;
753
krb5_storage *msg, *reply, *dreply;
754
OM_uint32 maj_stat, min_stat;
755
gss_buffer_desc gin, gout;
756
struct gctx gctx;
757
void *server_handle = NULL;
758
759
memset(&gctx, 0, sizeof(gctx));
760
761
msg = krb5_storage_emem();
762
reply = krb5_storage_emem();
763
dreply = krb5_storage_emem();
764
765
/*
766
* First packet comes partly from the caller
767
*/
768
769
INSIST(ilen >= 4);
770
771
while (1) {
772
struct call_header chdr;
773
struct gcred gcred;
774
uint32_t mtype;
775
krb5_data headercopy;
776
777
krb5_storage_truncate(dreply, 0);
778
krb5_storage_truncate(reply, 0);
779
krb5_storage_truncate(msg, 0);
780
781
krb5_data_zero(&headercopy);
782
memset(&chdr, 0, sizeof(chdr));
783
memset(&gcred, 0, sizeof(gcred));
784
785
/*
786
* This is very icky to handle the the auto-detection between
787
* the Heimdal protocol and the MIT ONC-RPC based protocol.
788
*/
789
790
if (ilen) {
791
int last_fragment;
792
unsigned long len;
793
ssize_t slen;
794
unsigned char tmp[4];
795
796
if (ilen < 4) {
797
memcpy(tmp, buf, ilen);
798
slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen);
799
INSIST((size_t)slen == sizeof(tmp) - ilen);
800
801
ilen = sizeof(tmp);
802
buf = tmp;
803
}
804
INSIST(ilen >= 4);
805
806
_krb5_get_int(buf, &len, 4);
807
last_fragment = (len & LAST_FRAGMENT) != 0;
808
len &= ~LAST_FRAGMENT;
809
810
ilen -= 4;
811
buf += 4;
812
813
if (ilen) {
814
if (len < ilen) {
815
slen = krb5_storage_write(msg, buf, len);
816
INSIST((size_t)slen == len);
817
ilen -= len;
818
len = 0;
819
} else {
820
slen = krb5_storage_write(msg, buf, ilen);
821
INSIST((size_t)slen == ilen);
822
len -= ilen;
823
}
824
}
825
826
CHECK(read_data(sp, msg, len));
827
828
if (!last_fragment) {
829
ret = collect_framents(sp, msg);
830
if (ret == HEIM_ERR_EOF)
831
krb5_errx(contextp, 0, "client disconnected");
832
INSIST(ret == 0);
833
}
834
} else {
835
836
ret = collect_framents(sp, msg);
837
if (ret == HEIM_ERR_EOF)
838
krb5_errx(contextp, 0, "client disconnected");
839
INSIST(ret == 0);
840
}
841
krb5_storage_seek(msg, 0, SEEK_SET);
842
843
CHECK(krb5_ret_uint32(msg, &chdr.xid));
844
CHECK(krb5_ret_uint32(msg, &mtype));
845
CHECK(krb5_ret_uint32(msg, &chdr.rpcvers));
846
CHECK(krb5_ret_uint32(msg, &chdr.prog));
847
CHECK(krb5_ret_uint32(msg, &chdr.vers));
848
CHECK(krb5_ret_uint32(msg, &chdr.proc));
849
CHECK(ret_auth_opaque(msg, &chdr.cred));
850
CHECK(copyheader(msg, &headercopy));
851
CHECK(ret_auth_opaque(msg, &chdr.verf));
852
853
INSIST(chdr.rpcvers == RPC_VERSION);
854
INSIST(chdr.prog == KADM_SERVER);
855
INSIST(chdr.vers == VVERSION);
856
INSIST(chdr.cred.flavor == FLAVOR_GSS);
857
858
CHECK(ret_gcred(&chdr.cred.data, &gcred));
859
860
INSIST(gcred.version == FLAVOR_GSS_VERSION);
861
862
if (gctx.done) {
863
INSIST(chdr.verf.flavor == FLAVOR_GSS);
864
865
/* from first byte to last of credential */
866
gin.value = headercopy.data;
867
gin.length = headercopy.length;
868
gout.value = chdr.verf.data.data;
869
gout.length = chdr.verf.data.length;
870
871
maj_stat = gss_verify_mic(&min_stat, gctx.ctx, &gin, &gout, NULL);
872
INSIST(maj_stat == GSS_S_COMPLETE);
873
}
874
875
switch(gcred.proc) {
876
case RPG_DATA: {
877
krb5_data data;
878
int conf_state;
879
uint32_t seq;
880
krb5_storage *sp1;
881
882
INSIST(gcred.service == rpg_privacy);
883
884
INSIST(gctx.done);
885
886
INSIST(krb5_data_cmp(&gcred.handle, &gctx.handle) == 0);
887
888
CHECK(ret_data_xdr(msg, &data));
889
890
gin.value = data.data;
891
gin.length = data.length;
892
893
maj_stat = gss_unwrap(&min_stat, gctx.ctx, &gin, &gout,
894
&conf_state, NULL);
895
krb5_data_free(&data);
896
INSIST(maj_stat == GSS_S_COMPLETE);
897
INSIST(conf_state != 0);
898
899
sp1 = krb5_storage_from_mem(gout.value, gout.length);
900
INSIST(sp1 != NULL);
901
902
CHECK(krb5_ret_uint32(sp1, &seq));
903
INSIST (seq == gcred.seq_num);
904
905
/*
906
* Check sequence number
907
*/
908
INSIST(seq > gctx.seq_num);
909
gctx.seq_num = seq;
910
911
/*
912
* If contextp is setup, priv data have the seq_num stored
913
* first in the block, so add it here before users data is
914
* added.
915
*/
916
CHECK(krb5_store_uint32(dreply, gctx.seq_num));
917
918
if (chdr.proc >= sizeof(procs)/sizeof(procs[0])) {
919
krb5_warnx(contextp, "proc number out of array");
920
} else if (procs[chdr.proc].func == NULL) {
921
krb5_warnx(contextp, "proc '%s' never implemented",
922
procs[chdr.proc].name);
923
} else {
924
krb5_warnx(contextp, "proc %s", procs[chdr.proc].name);
925
INSIST(server_handle != NULL);
926
(*procs[chdr.proc].func)(server_handle, sp, dreply);
927
}
928
krb5_storage_free(sp);
929
gss_release_buffer(&min_stat, &gout);
930
931
break;
932
}
933
case RPG_INIT:
934
INSIST(gctx.inprogress == 0);
935
INSIST(gctx.ctx == NULL);
936
937
gctx.inprogress = 1;
938
/* FALL THOUGH */
939
case RPG_CONTINUE_INIT: {
940
gss_name_t src_name = GSS_C_NO_NAME;
941
krb5_data in;
942
943
INSIST(gctx.inprogress);
944
945
CHECK(ret_data_xdr(msg, &in));
946
947
gin.value = in.data;
948
gin.length = in.length;
949
gout.value = NULL;
950
gout.length = 0;
951
952
maj_stat = gss_accept_sec_context(&min_stat,
953
&gctx.ctx,
954
GSS_C_NO_CREDENTIAL,
955
&gin,
956
GSS_C_NO_CHANNEL_BINDINGS,
957
&src_name,
958
NULL,
959
&gout,
960
NULL,
961
NULL,
962
NULL);
963
if (GSS_ERROR(maj_stat)) {
964
gss_print_errors(contextp, maj_stat, min_stat);
965
krb5_errx(contextp, 1, "gss error, exit");
966
}
967
if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) {
968
kadm5_config_params realm_params;
969
gss_buffer_desc bufp;
970
char *client;
971
972
gctx.done = 1;
973
974
memset(&realm_params, 0, sizeof(realm_params));
975
976
maj_stat = gss_export_name(&min_stat, src_name, &bufp);
977
INSIST(maj_stat == GSS_S_COMPLETE);
978
979
CHECK(parse_name(bufp.value, bufp.length,
980
GSS_KRB5_MECHANISM, &client));
981
982
gss_release_buffer(&min_stat, &bufp);
983
984
krb5_warnx(contextp, "%s connected", client);
985
986
ret = kadm5_s_init_with_password_ctx(contextp,
987
client,
988
NULL,
989
KADM5_ADMIN_SERVICE,
990
&realm_params,
991
0, 0,
992
&server_handle);
993
INSIST(ret == 0);
994
}
995
996
INSIST(gctx.ctx != GSS_C_NO_CONTEXT);
997
998
CHECK(krb5_store_uint32(dreply, 0));
999
CHECK(store_gss_init_res(dreply, gctx.handle,
1000
maj_stat, min_stat, 1, &gout));
1001
if (gout.value)
1002
gss_release_buffer(&min_stat, &gout);
1003
if (src_name)
1004
gss_release_name(&min_stat, &src_name);
1005
1006
break;
1007
}
1008
case RPG_DESTROY:
1009
krb5_errx(contextp, 1, "client destroyed gss contextp");
1010
default:
1011
krb5_errx(contextp, 1, "client sent unknown gsscode %d",
1012
(int)gcred.proc);
1013
}
1014
1015
krb5_data_free(&gcred.handle);
1016
krb5_data_free(&chdr.cred.data);
1017
krb5_data_free(&chdr.verf.data);
1018
krb5_data_free(&headercopy);
1019
1020
CHECK(krb5_store_uint32(reply, chdr.xid));
1021
CHECK(krb5_store_uint32(reply, 1)); /* REPLY */
1022
CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */
1023
1024
if (!gctx.done) {
1025
krb5_data data;
1026
1027
CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */
1028
CHECK(krb5_store_uint32(reply, 0)); /* length */
1029
1030
CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1031
1032
CHECK(krb5_storage_to_data(dreply, &data));
1033
INSIST((size_t)krb5_storage_write(reply, data.data, data.length) == data.length);
1034
krb5_data_free(&data);
1035
1036
} else {
1037
uint32_t seqnum = htonl(gctx.seq_num);
1038
krb5_data data;
1039
1040
gin.value = &seqnum;
1041
gin.length = sizeof(seqnum);
1042
1043
maj_stat = gss_get_mic(&min_stat, gctx.ctx, 0, &gin, &gout);
1044
INSIST(maj_stat == GSS_S_COMPLETE);
1045
1046
data.data = gout.value;
1047
data.length = gout.length;
1048
1049
CHECK(krb5_store_uint32(reply, FLAVOR_GSS));
1050
CHECK(store_data_xdr(reply, data));
1051
gss_release_buffer(&min_stat, &gout);
1052
1053
CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1054
1055
CHECK(krb5_storage_to_data(dreply, &data));
1056
1057
if (gctx.inprogress) {
1058
ssize_t sret;
1059
gctx.inprogress = 0;
1060
sret = krb5_storage_write(reply, data.data, data.length);
1061
INSIST((size_t)sret == data.length);
1062
krb5_data_free(&data);
1063
} else {
1064
int conf_state;
1065
1066
gin.value = data.data;
1067
gin.length = data.length;
1068
1069
maj_stat = gss_wrap(&min_stat, gctx.ctx, 1, 0,
1070
&gin, &conf_state, &gout);
1071
INSIST(maj_stat == GSS_S_COMPLETE);
1072
INSIST(conf_state != 0);
1073
krb5_data_free(&data);
1074
1075
data.data = gout.value;
1076
data.length = gout.length;
1077
1078
store_data_xdr(reply, data);
1079
gss_release_buffer(&min_stat, &gout);
1080
}
1081
}
1082
1083
{
1084
krb5_data data;
1085
ssize_t sret;
1086
CHECK(krb5_storage_to_data(reply, &data));
1087
CHECK(krb5_store_uint32(sp, data.length | LAST_FRAGMENT));
1088
sret = krb5_storage_write(sp, data.data, data.length);
1089
INSIST((size_t)sret == data.length);
1090
krb5_data_free(&data);
1091
}
1092
1093
}
1094
}
1095
1096
1097
int
1098
handle_mit(krb5_context contextp, void *buf, size_t len, krb5_socket_t sock)
1099
{
1100
krb5_storage *sp;
1101
1102
dcontext = contextp;
1103
1104
sp = krb5_storage_from_fd(sock);
1105
INSIST(sp != NULL);
1106
1107
process_stream(contextp, buf, len, sp);
1108
1109
return 0;
1110
}
1111
1112