Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/clients/kvno/kvno.c
34907 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/*
3
* Copyright (C) 1998 by the FundsXpress, INC.
4
*
5
* All rights reserved.
6
*
7
* Export of this software from the United States of America may require
8
* a specific license from the United States Government. It is the
9
* responsibility of any person or organization contemplating export to
10
* obtain such a license before exporting.
11
*
12
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13
* distribute this software and its documentation for any purpose and
14
* without fee is hereby granted, provided that the above copyright
15
* notice appear in all copies and that both that copyright notice and
16
* this permission notice appear in supporting documentation, and that
17
* the name of FundsXpress. not be used in advertising or publicity pertaining
18
* to distribution of the software without specific, written prior
19
* permission. FundsXpress makes no representations about the suitability of
20
* this software for any purpose. It is provided "as is" without express
21
* or implied warranty.
22
*
23
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26
*/
27
28
#include "k5-platform.h"
29
#include "k5-buf.h"
30
#include "k5-base64.h"
31
#include <locale.h>
32
#ifdef HAVE_UNISTD_H
33
#include <unistd.h>
34
#endif
35
#include <string.h>
36
#include <ctype.h>
37
38
static char *prog;
39
static int quiet = 0;
40
41
static void
42
xusage(void)
43
{
44
fprintf(stderr, _("usage: %s [-c ccache] [-e etype] [-k keytab] [-q] "
45
"[-u | -S sname]\n"
46
"\t[[{-F cert_file | {-I | -U} for_user} [-P]] | "
47
"--u2u ccache]\n"
48
"\t[--cached-only] [--no-store] [--out-cache] "
49
"service1 service2 ...\n"),
50
prog);
51
exit(1);
52
}
53
54
static void do_v5_kvno(int argc, char *argv[], char *ccachestr, char *etypestr,
55
char *keytab_name, char *sname, int cached_only,
56
int canon, int no_store, int unknown, char *for_user,
57
int for_user_enterprise, char *for_user_cert_file,
58
int proxy, const char *out_ccname,
59
const char *u2u_ccname);
60
61
#include <com_err.h>
62
static void extended_com_err_fn(const char *myprog, errcode_t code,
63
const char *fmt, va_list args);
64
65
int
66
main(int argc, char *argv[])
67
{
68
enum { OPTION_U2U = 256, OPTION_OUT_CACHE = 257 };
69
const char *shopts = "uCc:e:hk:qPS:I:U:F:";
70
int option;
71
char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL;
72
char *sname = NULL, *for_user = NULL, *u2u_ccname = NULL;
73
char *for_user_cert_file = NULL, *out_ccname = NULL;
74
int canon = 0, unknown = 0, proxy = 0, for_user_enterprise = 0;
75
int impersonate = 0, cached_only = 0, no_store = 0;
76
struct option lopts[] = {
77
{ "cached-only", 0, &cached_only, 1 },
78
{ "no-store", 0, &no_store, 1 },
79
{ "out-cache", 1, NULL, OPTION_OUT_CACHE },
80
{ "u2u", 1, NULL, OPTION_U2U },
81
{ NULL, 0, NULL, 0 }
82
};
83
84
setlocale(LC_ALL, "");
85
set_com_err_hook(extended_com_err_fn);
86
87
prog = strrchr(argv[0], '/');
88
prog = prog ? (prog + 1) : argv[0];
89
90
while ((option = getopt_long(argc, argv, shopts, lopts, NULL)) != -1) {
91
switch (option) {
92
case 'C':
93
canon = 1;
94
break;
95
case 'c':
96
ccachestr = optarg;
97
break;
98
case 'e':
99
etypestr = optarg;
100
break;
101
case 'h':
102
xusage();
103
break;
104
case 'k':
105
keytab_name = optarg;
106
break;
107
case 'q':
108
quiet = 1;
109
break;
110
case 'P':
111
proxy = 1; /* S4U2Proxy - constrained delegation */
112
break;
113
case 'S':
114
sname = optarg;
115
if (unknown == 1) {
116
fprintf(stderr,
117
_("Options -u and -S are mutually exclusive\n"));
118
xusage();
119
}
120
break;
121
case 'u':
122
unknown = 1;
123
if (sname != NULL) {
124
fprintf(stderr,
125
_("Options -u and -S are mutually exclusive\n"));
126
xusage();
127
}
128
break;
129
case 'I':
130
impersonate = 1;
131
for_user = optarg;
132
break;
133
case 'U':
134
impersonate = 1;
135
for_user_enterprise = 1;
136
for_user = optarg;
137
break;
138
case 'F':
139
impersonate = 1;
140
for_user_cert_file = optarg;
141
break;
142
case OPTION_U2U:
143
u2u_ccname = optarg;
144
break;
145
case OPTION_OUT_CACHE:
146
out_ccname = optarg;
147
break;
148
case 0:
149
/* If this option set a flag, do nothing else now. */
150
break;
151
default:
152
xusage();
153
break;
154
}
155
}
156
157
if (u2u_ccname != NULL && impersonate) {
158
fprintf(stderr,
159
_("Options --u2u and -I|-U|-F are mutually exclusive\n"));
160
xusage();
161
}
162
163
if (proxy) {
164
if (!impersonate) {
165
fprintf(stderr, _("Option -P (constrained delegation) requires "
166
"option -I|-U|-F (protocol transition)\n"));
167
xusage();
168
}
169
}
170
171
if (argc - optind < 1)
172
xusage();
173
174
do_v5_kvno(argc - optind, argv + optind, ccachestr, etypestr, keytab_name,
175
sname, cached_only, canon, no_store, unknown, for_user,
176
for_user_enterprise, for_user_cert_file, proxy, out_ccname,
177
u2u_ccname);
178
return 0;
179
}
180
181
#include <k5-int.h>
182
static krb5_context context;
183
static void extended_com_err_fn(const char *myprog, errcode_t code,
184
const char *fmt, va_list args)
185
{
186
const char *emsg;
187
188
emsg = krb5_get_error_message(context, code);
189
fprintf(stderr, "%s: %s ", myprog, emsg);
190
krb5_free_error_message(context, emsg);
191
vfprintf(stderr, fmt, args);
192
fprintf(stderr, "\n");
193
}
194
195
/* Read a line from fp into buf. Trim any trailing whitespace, and return a
196
* pointer to the first non-whitespace character. */
197
static const char *
198
read_line(FILE *fp, char *buf, size_t bufsize)
199
{
200
char *end, *begin;
201
202
if (fgets(buf, bufsize, fp) == NULL)
203
return NULL;
204
205
end = buf + strlen(buf);
206
while (end > buf && isspace((uint8_t)end[-1]))
207
*--end = '\0';
208
209
begin = buf;
210
while (isspace((uint8_t)*begin))
211
begin++;
212
213
return begin;
214
}
215
216
/* Read a certificate from file_name in PEM format, placing the DER
217
* representation of the certificate in *der_out. */
218
static krb5_error_code
219
read_pem_file(char *file_name, krb5_data *der_out)
220
{
221
krb5_error_code ret = 0;
222
FILE *fp = NULL;
223
const char *begin_line = "-----BEGIN CERTIFICATE-----";
224
const char *end_line = "-----END ", *line;
225
char linebuf[256], *b64;
226
struct k5buf buf = EMPTY_K5BUF;
227
uint8_t *der_cert;
228
size_t dlen;
229
230
*der_out = empty_data();
231
232
fp = fopen(file_name, "r");
233
if (fp == NULL)
234
return errno;
235
236
for (;;) {
237
line = read_line(fp, linebuf, sizeof(linebuf));
238
if (line == NULL) {
239
ret = EINVAL;
240
k5_setmsg(context, ret, _("No begin line not found"));
241
goto cleanup;
242
}
243
if (strncmp(line, begin_line, strlen(begin_line)) == 0)
244
break;
245
}
246
247
k5_buf_init_dynamic(&buf);
248
for (;;) {
249
line = read_line(fp, linebuf, sizeof(linebuf));
250
if (line == NULL) {
251
ret = EINVAL;
252
k5_setmsg(context, ret, _("No end line found"));
253
goto cleanup;
254
}
255
256
if (strncmp(line, end_line, strlen(end_line)) == 0)
257
break;
258
259
/* Header lines would be expected for an actual privacy-enhanced mail
260
* message, but not for a certificate. */
261
if (*line == '\0' || strchr(line, ':') != NULL) {
262
ret = EINVAL;
263
k5_setmsg(context, ret, _("Unexpected header line"));
264
goto cleanup;
265
}
266
267
k5_buf_add(&buf, line);
268
}
269
270
b64 = k5_buf_cstring(&buf);
271
if (b64 == NULL) {
272
ret = ENOMEM;
273
goto cleanup;
274
}
275
der_cert = k5_base64_decode(b64, &dlen);
276
if (der_cert == NULL) {
277
ret = EINVAL;
278
k5_setmsg(context, ret, _("Invalid base64"));
279
goto cleanup;
280
}
281
282
*der_out = make_data(der_cert, dlen);
283
284
cleanup:
285
fclose(fp);
286
k5_buf_free(&buf);
287
return ret;
288
}
289
290
/* Request a single service ticket and display its status (unless quiet is
291
* set). On failure, display an error message and return non-zero. */
292
static krb5_error_code
293
kvno(const char *name, krb5_ccache ccache, krb5_principal me,
294
krb5_enctype etype, krb5_keytab keytab, const char *sname,
295
krb5_flags options, int unknown, krb5_principal for_user_princ,
296
krb5_data *for_user_cert, int proxy, krb5_data *u2u_ticket,
297
krb5_creds **creds_out)
298
{
299
krb5_error_code ret;
300
krb5_principal server = NULL;
301
krb5_ticket *ticket = NULL;
302
krb5_creds in_creds, *creds = NULL;
303
char *princ = NULL;
304
305
*creds_out = NULL;
306
memset(&in_creds, 0, sizeof(in_creds));
307
308
if (sname != NULL) {
309
ret = krb5_sname_to_principal(context, name, sname, KRB5_NT_SRV_HST,
310
&server);
311
} else {
312
ret = krb5_parse_name(context, name, &server);
313
}
314
if (ret) {
315
if (!quiet)
316
com_err(prog, ret, _("while parsing principal name %s"), name);
317
goto cleanup;
318
}
319
if (unknown)
320
krb5_princ_type(context, server) = KRB5_NT_UNKNOWN;
321
322
ret = krb5_unparse_name(context, server, &princ);
323
if (ret) {
324
com_err(prog, ret, _("while formatting parsed principal name for "
325
"'%s'"), name);
326
goto cleanup;
327
}
328
329
in_creds.keyblock.enctype = etype;
330
331
if (u2u_ticket != NULL)
332
in_creds.second_ticket = *u2u_ticket;
333
334
if (for_user_princ != NULL || for_user_cert != NULL) {
335
if (!proxy && !krb5_principal_compare(context, me, server)) {
336
ret = EINVAL;
337
com_err(prog, ret,
338
_("client and server principal names must match"));
339
goto cleanup;
340
}
341
342
in_creds.client = for_user_princ;
343
in_creds.server = me;
344
ret = krb5_get_credentials_for_user(context, options, ccache,
345
&in_creds, for_user_cert, &creds);
346
} else {
347
in_creds.client = me;
348
in_creds.server = server;
349
ret = krb5_get_credentials(context, options, ccache, &in_creds,
350
&creds);
351
}
352
353
if (ret) {
354
com_err(prog, ret, _("while getting credentials for %s"), princ);
355
goto cleanup;
356
}
357
358
/* We need a native ticket. */
359
ret = krb5_decode_ticket(&creds->ticket, &ticket);
360
if (ret) {
361
com_err(prog, ret, _("while decoding ticket for %s"), princ);
362
goto cleanup;
363
}
364
365
if (keytab != NULL) {
366
ret = krb5_server_decrypt_ticket_keytab(context, keytab, ticket);
367
if (ret) {
368
if (!quiet) {
369
fprintf(stderr, "%s: kvno = %d, keytab entry invalid\n", princ,
370
ticket->enc_part.kvno);
371
}
372
com_err(prog, ret, _("while decrypting ticket for %s"), princ);
373
goto cleanup;
374
}
375
if (!quiet) {
376
printf(_("%s: kvno = %d, keytab entry valid\n"), princ,
377
ticket->enc_part.kvno);
378
}
379
} else {
380
if (!quiet)
381
printf(_("%s: kvno = %d\n"), princ, ticket->enc_part.kvno);
382
}
383
384
if (proxy) {
385
in_creds.client = creds->client;
386
creds->client = NULL;
387
krb5_free_creds(context, creds);
388
creds = NULL;
389
in_creds.server = server;
390
391
ret = krb5_get_credentials_for_proxy(context, KRB5_GC_CANONICALIZE,
392
ccache, &in_creds, ticket,
393
&creds);
394
krb5_free_principal(context, in_creds.client);
395
if (ret) {
396
com_err(prog, ret, _("%s: constrained delegation failed"),
397
princ);
398
goto cleanup;
399
}
400
}
401
402
*creds_out = creds;
403
creds = NULL;
404
405
cleanup:
406
krb5_free_principal(context, server);
407
krb5_free_ticket(context, ticket);
408
krb5_free_creds(context, creds);
409
krb5_free_unparsed_name(context, princ);
410
return ret;
411
}
412
413
/* Fetch the encoded local TGT for ccname's default client principal. */
414
static krb5_error_code
415
get_u2u_ticket(const char *ccname, krb5_data **ticket_out)
416
{
417
krb5_error_code ret;
418
krb5_ccache cc = NULL;
419
krb5_creds mcred, *creds = NULL;
420
421
*ticket_out = NULL;
422
memset(&mcred, 0, sizeof(mcred));
423
424
ret = krb5_cc_resolve(context, ccname, &cc);
425
if (ret)
426
goto cleanup;
427
ret = krb5_cc_get_principal(context, cc, &mcred.client);
428
if (ret)
429
goto cleanup;
430
ret = krb5_build_principal_ext(context, &mcred.server,
431
mcred.client->realm.length,
432
mcred.client->realm.data,
433
KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
434
mcred.client->realm.length,
435
mcred.client->realm.data, 0);
436
if (ret)
437
goto cleanup;
438
ret = krb5_get_credentials(context, KRB5_GC_CACHED, cc, &mcred, &creds);
439
if (ret)
440
goto cleanup;
441
442
ret = krb5_copy_data(context, &creds->ticket, ticket_out);
443
444
cleanup:
445
if (cc != NULL)
446
krb5_cc_close(context, cc);
447
krb5_free_cred_contents(context, &mcred);
448
krb5_free_creds(context, creds);
449
return ret;
450
}
451
452
static void
453
do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr,
454
char *keytab_name, char *sname, int cached_only, int canon,
455
int no_store, int unknown, char *for_user, int for_user_enterprise,
456
char *for_user_cert_file, int proxy, const char *out_ccname,
457
const char *u2u_ccname)
458
{
459
krb5_error_code ret;
460
int i, errors, flags, initialized = 0;
461
krb5_enctype etype;
462
krb5_ccache ccache, mcc, out_ccache = NULL;
463
krb5_principal me;
464
krb5_keytab keytab = NULL;
465
krb5_principal for_user_princ = NULL;
466
krb5_flags options = 0;
467
krb5_data cert_data = empty_data(), *user_cert = NULL, *u2u_ticket = NULL;
468
krb5_creds *creds;
469
470
if (canon)
471
options |= KRB5_GC_CANONICALIZE;
472
if (cached_only)
473
options |= KRB5_GC_CACHED;
474
if (no_store || out_ccname != NULL)
475
options |= KRB5_GC_NO_STORE;
476
477
ret = krb5_init_context(&context);
478
if (ret) {
479
com_err(prog, ret, _("while initializing krb5 library"));
480
exit(1);
481
}
482
483
if (etypestr) {
484
ret = krb5_string_to_enctype(etypestr, &etype);
485
if (ret) {
486
com_err(prog, ret, _("while converting etype"));
487
exit(1);
488
}
489
} else {
490
etype = 0;
491
}
492
493
if (ccachestr)
494
ret = krb5_cc_resolve(context, ccachestr, &ccache);
495
else
496
ret = krb5_cc_default(context, &ccache);
497
if (ret) {
498
com_err(prog, ret, _("while opening ccache"));
499
exit(1);
500
}
501
502
if (out_ccname != NULL) {
503
ret = krb5_cc_resolve(context, out_ccname, &out_ccache);
504
if (ret) {
505
com_err(prog, ret, _("while resolving output ccache"));
506
exit(1);
507
}
508
}
509
510
if (keytab_name != NULL) {
511
ret = krb5_kt_resolve(context, keytab_name, &keytab);
512
if (ret) {
513
com_err(prog, ret, _("resolving keytab %s"), keytab_name);
514
exit(1);
515
}
516
}
517
518
if (for_user) {
519
flags = for_user_enterprise ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
520
ret = krb5_parse_name_flags(context, for_user, flags, &for_user_princ);
521
if (ret) {
522
com_err(prog, ret, _("while parsing principal name %s"), for_user);
523
exit(1);
524
}
525
}
526
527
if (for_user_cert_file != NULL) {
528
ret = read_pem_file(for_user_cert_file, &cert_data);
529
if (ret) {
530
com_err(prog, ret, _("while reading certificate file %s"),
531
for_user_cert_file);
532
exit(1);
533
}
534
user_cert = &cert_data;
535
}
536
537
if (u2u_ccname != NULL) {
538
ret = get_u2u_ticket(u2u_ccname, &u2u_ticket);
539
if (ret) {
540
com_err(prog, ret, _("while getting user-to-user ticket from %s"),
541
u2u_ccname);
542
exit(1);
543
}
544
options |= KRB5_GC_USER_USER;
545
}
546
547
ret = krb5_cc_get_principal(context, ccache, &me);
548
if (ret) {
549
com_err(prog, ret, _("while getting client principal name"));
550
exit(1);
551
}
552
553
if (out_ccache != NULL) {
554
ret = krb5_cc_new_unique(context, "MEMORY", NULL, &mcc);
555
if (ret) {
556
com_err(prog, ret, _("while creating temporary output ccache"));
557
exit(1);
558
}
559
}
560
561
errors = 0;
562
for (i = 0; i < count; i++) {
563
if (kvno(names[i], ccache, me, etype, keytab, sname, options, unknown,
564
for_user_princ, user_cert, proxy, u2u_ticket, &creds) != 0) {
565
errors++;
566
} else if (out_ccache != NULL) {
567
if (!initialized) {
568
ret = krb5_cc_initialize(context, mcc, creds->client);
569
if (ret) {
570
com_err(prog, ret, _("while initializing output ccache"));
571
exit(1);
572
}
573
initialized = 1;
574
}
575
if (count == 1)
576
ret = k5_cc_store_primary_cred(context, mcc, creds);
577
else
578
ret = krb5_cc_store_cred(context, mcc, creds);
579
if (ret) {
580
com_err(prog, ret, _("while storing creds in output ccache"));
581
exit(1);
582
}
583
}
584
585
krb5_free_creds(context, creds);
586
}
587
588
if (!errors && out_ccache != NULL) {
589
ret = krb5_cc_move(context, mcc, out_ccache);
590
if (ret) {
591
com_err(prog, ret, _("while writing output ccache"));
592
exit(1);
593
}
594
}
595
596
if (keytab != NULL)
597
krb5_kt_close(context, keytab);
598
krb5_free_principal(context, me);
599
krb5_free_principal(context, for_user_princ);
600
krb5_cc_close(context, ccache);
601
krb5_free_data(context, u2u_ticket);
602
krb5_free_data_contents(context, &cert_data);
603
krb5_free_context(context);
604
605
if (errors)
606
exit(1);
607
608
exit(0);
609
}
610
611