Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * $Id$ | |
3 | * | |
4 | * Copyright 1990,1991 by the Massachusetts Institute of Technology | |
5 | * For distribution and copying rights, see the file "mit-copyright.h" | |
6 | */ | |
7 | /* | |
8 | * Copyright (c) 2005, 2006 | |
9 | * The Linux Box Corporation | |
10 | * ALL RIGHTS RESERVED | |
11 | * | |
12 | * Permission is granted to use, copy, create derivative works | |
13 | * and redistribute this software and such derivative works | |
14 | * for any purpose, so long as the name of the Linux Box | |
15 | * Corporation is not used in any advertising or publicity | |
16 | * pertaining to the use or distribution of this software | |
17 | * without specific, written prior authorization. If the | |
18 | * above copyright notice or any other identification of the | |
19 | * Linux Box Corporation is included in any copy of any | |
20 | * portion of this software, then the disclaimer below must | |
21 | * also be included. | |
22 | * | |
23 | * This software is provided as is, without representation | |
24 | * from the Linux Box Corporation as to its fitness for any | |
25 | * purpose, and without warranty by the Linux Box Corporation | |
26 | * of any kind, either express or implied, including | |
27 | * without limitation the implied warranties of | |
28 | * merchantability and fitness for a particular purpose. The | |
29 | * regents of the Linux Box Corporation shall not be liable | |
30 | * for any damages, including special, indirect, incidental, or | |
31 | * consequential damages, with respect to any claim arising | |
32 | * out of or in connection with the use of the software, even | |
33 | * if it has been or is hereafter advised of the possibility of | |
34 | * such damages. | |
35 | */ | |
36 | ||
37 | #include <afsconfig.h> | |
38 | #include <afs/param.h> | |
39 | #include <afs/stds.h> | |
40 | ||
41 | #include <roken.h> | |
42 | ||
43 | #include <ctype.h> | |
44 | ||
45 | #include <afs/ktc.h> | |
46 | #include <afs/token.h> | |
47 | ||
48 | #define KERBEROS_APPLE_DEPRECATED(x) | |
49 | #include <krb5.h> | |
50 | #ifdef HAVE_COM_ERR_H | |
51 | # include <com_err.h> | |
52 | #elif HAVE_ET_COM_ERR_H | |
53 | # include <et/com_err.h> | |
54 | #elif HAVE_KRB5_COM_ERR_H | |
55 | # include <krb5/com_err.h> | |
56 | #else | |
57 | # error No com_err.h? We need some kind of com_err.h | |
58 | #endif | |
59 | ||
60 | #ifndef HAVE_KERBEROSV_HEIM_ERR_H | |
61 | #include <afs/com_err.h> | |
62 | #endif | |
63 | ||
64 | #ifdef AFS_SUN5_ENV | |
65 | #include <sys/ioccom.h> | |
66 | #endif | |
67 | ||
68 | #include <afs/auth.h> | |
69 | #include <afs/cellconfig.h> | |
70 | #include <afs/vice.h> | |
71 | #include <afs/venus.h> | |
72 | #include <afs/ptserver.h> | |
73 | #include <afs/ptuser.h> | |
74 | #include <afs/pterror.h> | |
75 | #include <afs/dirpath.h> | |
76 | #include <afs/afsutil.h> | |
77 | ||
78 | #include "aklog.h" | |
79 | #include "linked_list.h" | |
80 | ||
81 | #ifdef HAVE_KRB5_CREDS_KEYBLOCK | |
82 | #define USING_MIT 1 | |
83 | #endif | |
84 | #ifdef HAVE_KRB5_CREDS_SESSION | |
85 | #define USING_HEIMDAL 1 | |
86 | #endif | |
87 | ||
88 | #define AFSKEY "afs" | |
89 | #define AFSINST "" | |
90 | ||
91 | #ifndef AFS_TRY_FULL_PRINC | |
92 | #define AFS_TRY_FULL_PRINC 1 | |
93 | #endif /* AFS_TRY_FULL_PRINC */ | |
94 | ||
95 | #define AKLOG_TRYAGAIN -1 | |
96 | #define AKLOG_SUCCESS 0 | |
97 | #define AKLOG_USAGE 1 | |
98 | #define AKLOG_SOMETHINGSWRONG 2 | |
99 | #define AKLOG_AFS 3 | |
100 | #define AKLOG_KERBEROS 4 | |
101 | #define AKLOG_TOKEN 5 | |
102 | #define AKLOG_BADPATH 6 | |
103 | #define AKLOG_MISC 7 | |
104 | ||
105 | #ifndef TRUE | |
106 | #define TRUE 1 | |
107 | #endif | |
108 | ||
109 | #ifndef FALSE | |
110 | #define FALSE 0 | |
111 | #endif | |
112 | ||
113 | #ifndef MAXSYMLINKS | |
114 | /* RedHat 4.x doesn't seem to define this */ | |
115 | #define MAXSYMLINKS 5 | |
116 | #endif | |
117 | ||
118 | #define DIR '/' /* Character that divides directories */ | |
119 | #define DIRSTRING "/" /* String form of above */ | |
120 | #define VOLMARKER ':' /* Character separating cellname from mntpt */ | |
121 | #define VOLMARKERSTRING ":" /* String form of above */ | |
122 | ||
123 | typedef struct { | |
124 | char cell[BUFSIZ]; | |
125 | char realm[REALM_SZ]; | |
126 | } cellinfo_t; | |
127 | ||
128 | static krb5_ccache _krb425_ccache = NULL; | |
129 | ||
130 | /* | |
131 | * Why doesn't AFS provide these prototypes? | |
132 | */ | |
133 | ||
134 | extern int pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32); | |
135 | ||
136 | /* | |
137 | * Other prototypes | |
138 | */ | |
139 | ||
140 | extern char *afs_realm_of_cell(krb5_context, struct afsconf_cell *, int); | |
141 | static int isdir(char *, unsigned char *); | |
142 | static krb5_error_code get_credv5(krb5_context context, char *, char *, | |
143 | char *, krb5_creds **); | |
144 | static int get_user_realm(krb5_context, char **); | |
145 | ||
146 | #define TRYAGAIN(x) (x == AKLOG_TRYAGAIN || \ | |
147 | x == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || \ | |
148 | x == KRB5KRB_ERR_GENERIC) | |
149 | ||
150 | #if defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size) | |
151 | ||
152 | #define get_princ_str(c, p, n) krb5_princ_component(c, p, n)->data | |
153 | #define get_princ_len(c, p, n) krb5_princ_component(c, p, n)->length | |
154 | #define second_comp(c, p) (krb5_princ_size(c, p) > 1) | |
155 | #define realm_data(c, p) krb5_princ_realm(c, p)->data | |
156 | #define realm_len(c, p) krb5_princ_realm(c, p)->length | |
157 | ||
158 | #elif defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) | |
159 | ||
160 | #define get_princ_str(c, p, n) krb5_principal_get_comp_string(c, p, n) | |
161 | #define get_princ_len(c, p, n) strlen(krb5_principal_get_comp_string(c, p, n)) | |
162 | #define second_comp(c, p) (krb5_principal_get_comp_string(c, p, 1) != NULL) | |
163 | #define realm_data(c, p) krb5_realm_data(krb5_principal_get_realm(c, p)) | |
164 | #define realm_len(c, p) krb5_realm_length(krb5_principal_get_realm(c, p)) | |
165 | ||
166 | #else | |
167 | #error "Must have either krb5_princ_size or krb5_principal_get_comp_string" | |
168 | #endif | |
169 | ||
170 | #if !defined(HAVE_KRB5_ENCRYPT_TKT_PART) && defined(HAVE_ENCODE_KRB5_ENC_TKT_PART) && defined(HAVE_KRB5_C_ENCRYPT) | |
171 | extern krb5_error_code encode_krb5_enc_tkt_part (const krb5_enc_tkt_part *rep, | |
172 | krb5_data **code); | |
173 | ||
174 | krb5_error_code | |
175 | krb5_encrypt_tkt_part(krb5_context context, | |
176 | const krb5_keyblock *key, | |
177 | krb5_ticket *ticket) | |
178 | { | |
179 | krb5_data *data = 0; | |
180 | int code; | |
181 | size_t enclen; | |
182 | ||
183 | if ((code = encode_krb5_enc_tkt_part(ticket->enc_part2, &data))) | |
184 | goto Done; | |
185 | if ((code = krb5_c_encrypt_length(context, key->enctype, | |
186 | data->length, &enclen))) | |
187 | goto Done; | |
188 | ticket->enc_part.ciphertext.length = enclen; | |
189 | if (!(ticket->enc_part.ciphertext.data = malloc(enclen))) { | |
190 | code = ENOMEM; | |
191 | goto Done; | |
192 | } | |
193 | if ((code = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_KDC_REP_TICKET, | |
194 | 0, data, &ticket->enc_part))) { | |
195 | free(ticket->enc_part.ciphertext.data); | |
196 | ticket->enc_part.ciphertext.data = 0; | |
197 | } | |
198 | Done: | |
199 | if (data) { | |
200 | if (data->data) | |
201 | free(data->data); | |
202 | free(data); | |
203 | } | |
204 | return code; | |
205 | } | |
206 | #endif | |
207 | ||
208 | #if defined(HAVE_KRB5_CREDS_KEYBLOCK) | |
209 | ||
210 | #define get_cred_keydata(c) c->keyblock.contents | |
211 | #define get_cred_keylen(c) c->keyblock.length | |
212 | #define get_creds_enctype(c) c->keyblock.enctype | |
213 | ||
214 | #elif defined(HAVE_KRB5_CREDS_SESSION) | |
215 | ||
216 | #define get_cred_keydata(c) c->session.keyvalue.data | |
217 | #define get_cred_keylen(c) c->session.keyvalue.length | |
218 | #define get_creds_enctype(c) c->session.keytype | |
219 | ||
220 | #else | |
221 | #error "Must have either keyblock or session member of krb5_creds" | |
222 | #endif | |
223 | ||
224 | /* MITKerberosShim logs but returns success */ | |
225 | #if !defined(HAVE_KRB5_524_CONV_PRINCIPAL) || defined(AFS_DARWIN110_ENV) || (!defined(HAVE_KRB5_524_CONVERT_CREDS) && !defined(HAVE_KRB524_CONVERT_CREDS_KDC)) | |
226 | #define HAVE_NO_KRB5_524 | |
227 | #elif !defined(HAVE_KRB5_524_CONVERT_CREDS) && defined(HAVE_KRB524_CONVERT_CREDS_KDC) | |
228 | #define krb5_524_convert_creds krb524_convert_creds_kdc | |
229 | #endif | |
230 | ||
231 | #if USING_HEIMDAL | |
232 | #define deref_keyblock_enctype(kb) \ | |
233 | ((kb)->keytype) | |
234 | ||
235 | #define deref_entry_keyblock(entry) \ | |
236 | entry->keyblock | |
237 | ||
238 | #define deref_session_key(creds) \ | |
239 | creds->session | |
240 | ||
241 | #define deref_enc_tkt_addrs(tkt) \ | |
242 | tkt->caddr | |
243 | ||
244 | #define deref_enc_length(enc) \ | |
245 | ((enc)->cipher.length) | |
246 | ||
247 | #define deref_enc_data(enc) \ | |
248 | ((enc)->cipher.data) | |
249 | ||
250 | #define krb5_free_keytab_entry_contents krb5_kt_free_entry | |
251 | ||
252 | #else | |
253 | #define deref_keyblock_enctype(kb) \ | |
254 | ((kb)->enctype) | |
255 | ||
256 | #define deref_entry_keyblock(entry) \ | |
257 | entry->key | |
258 | ||
259 | #define deref_session_key(creds) \ | |
260 | creds->keyblock | |
261 | ||
262 | #define deref_enc_tkt_addrs(tkt) \ | |
263 | tkt->caddrs | |
264 | ||
265 | #define deref_enc_length(enc) \ | |
266 | ((enc)->ciphertext.length) | |
267 | ||
268 | #define deref_enc_data(enc) \ | |
269 | ((enc)->ciphertext.data) | |
270 | ||
271 | #endif | |
272 | ||
273 | #define deref_entry_enctype(entry) \ | |
274 | deref_keyblock_enctype(&deref_entry_keyblock(entry)) | |
275 | ||
276 | /* | |
277 | * Provide a replacement for strerror if we don't have it | |
278 | */ | |
279 | ||
280 | #ifndef HAVE_STRERROR | |
281 | extern char *sys_errlist[]; | |
282 | #define strerror(x) sys_errlist[x] | |
283 | #endif /* HAVE_STRERROR */ | |
284 | ||
285 | static char *progname = NULL; /* Name of this program */ | |
286 | static int dflag = FALSE; /* Give debugging information */ | |
287 | static int noauth = FALSE; /* If true, don't try to get tokens */ | |
288 | static int zsubs = FALSE; /* Are we keeping track of zephyr subs? */ | |
289 | static int hosts = FALSE; /* Are we keeping track of hosts? */ | |
290 | static int noprdb = FALSE; /* Skip resolving name to id? */ | |
291 | static int linked = FALSE; /* try for both AFS nodes */ | |
292 | static int afssetpag = FALSE; /* setpag for AFS */ | |
293 | static int force = FALSE; /* Bash identical tokens? */ | |
294 | static int do524 = FALSE; /* Should we do 524 instead of rxkad2b? */ | |
295 | static char *keytab = NULL; /* keytab for akimpersonate */ | |
296 | static char *client = NULL; /* client principal for akimpersonate */ | |
297 | static linked_list zsublist; /* List of zephyr subscriptions */ | |
298 | static linked_list hostlist; /* List of host addresses */ | |
299 | static linked_list authedcells; /* List of cells already logged to */ | |
300 | ||
301 | /* A com_error bodge. The idea here is that this routine lets us lookup | |
302 | * things in the system com_err, if the AFS one just tells us the error | |
303 | * is unknown | |
304 | */ | |
305 | ||
306 | void | |
307 | redirect_errors(const char *who, afs_int32 code, const char *fmt, va_list ap) | |
308 | { | |
309 | if (who) { | |
310 | fputs(who, stderr); | |
311 | fputs(": ", stderr); | |
312 | } | |
313 | if (code) { | |
314 | const char *str = afs_error_message(code); | |
315 | if (strncmp(str, "unknown", strlen("unknown")) == 0) { | |
316 | #ifdef HAVE_KRB5_SVC_GET_MSG | |
317 | krb5_svc_get_msg(code,&str); | |
318 | #elif defined(HAVE_KRB5_GET_ERROR_MESSAGE) | |
319 | krb5_context context; | |
320 | krb5_init_context(&context); | |
321 | str = krb5_get_error_message(context, code); | |
322 | krb5_free_context(context); | |
323 | #else | |
324 | ; /* IRIX apparently has neither: use the string we have */ | |
325 | #endif | |
326 | } | |
327 | fputs(str, stderr); | |
328 | fputs(" ", stderr); | |
329 | #ifdef HAVE_KRB5_SVC_GET_MSG | |
330 | krb5_free_string(str); | |
331 | #endif | |
332 | } | |
333 | if (fmt) { | |
334 | vfprintf(stderr, fmt, ap); | |
335 | } | |
336 | putc('\n', stderr); | |
337 | fflush(stderr); | |
338 | } | |
339 | ||
340 | static void | |
341 | afs_dprintf(char *fmt, ...) { | |
342 | va_list ap; | |
343 | ||
344 | va_start(ap, fmt); | |
345 | if (dflag) | |
346 | vprintf(fmt, ap); | |
347 | va_end(ap); | |
348 | } | |
349 | ||
350 | static char * | |
351 | copy_cellinfo(cellinfo_t *cellinfo) | |
352 | { | |
353 | cellinfo_t *new_cellinfo; | |
354 | ||
355 | if ((new_cellinfo = malloc(sizeof(cellinfo_t)))) | |
356 | memcpy(new_cellinfo, cellinfo, sizeof(cellinfo_t)); | |
357 | ||
358 | return ((char *)new_cellinfo); | |
359 | } | |
360 | ||
361 | ||
362 | static int | |
363 | get_cellconfig(const char *config, char *cell, | |
364 | struct afsconf_cell *cellconfig, char **local_cell) | |
365 | { | |
366 | int status = AKLOG_SUCCESS; | |
367 | struct afsconf_dir *configdir; | |
368 | ||
369 | memset(cellconfig, 0, sizeof(*cellconfig)); | |
370 | ||
371 | *local_cell = malloc(MAXCELLCHARS); | |
372 | if (*local_cell == NULL) { | |
373 | fprintf(stderr, "%s: can't allocate memory for local cell name\n", | |
374 | progname); | |
375 | exit(AKLOG_AFS); | |
376 | } | |
377 | ||
378 | if (!(configdir = afsconf_Open(config))) { | |
379 | fprintf(stderr, | |
380 | "%s: can't get afs configuration (afsconf_Open(%s))\n", | |
381 | progname, config); | |
382 | exit(AKLOG_AFS); | |
383 | } | |
384 | ||
385 | if (cell != NULL && cell[0] == '\0') { | |
386 | /* Use the local cell */ | |
387 | cell = NULL; | |
388 | } | |
389 | ||
390 | /* XXX - This function modifies 'cell' by passing it through lcstring */ | |
391 | if (afsconf_GetCellInfo(configdir, cell, NULL, cellconfig)) { | |
392 | if (cell != NULL) { | |
393 | fprintf(stderr, "%s: Can't get information about cell %s.\n", | |
394 | progname, cell); | |
395 | } else { | |
396 | fprintf(stderr, "%s: Can't get information about the local cell.\n", | |
397 | progname); | |
398 | } | |
399 | status = AKLOG_AFS; | |
400 | } else if (afsconf_GetLocalCell(configdir, *local_cell, MAXCELLCHARS)) { | |
401 | fprintf(stderr, "%s: can't determine local cell.\n", progname); | |
402 | exit(AKLOG_AFS); | |
403 | } | |
404 | ||
405 | afsconf_Close(configdir); | |
406 | ||
407 | return(status); | |
408 | } | |
409 | ||
410 | static char * | |
411 | extract_realm(krb5_context context, krb5_principal princ) { | |
412 | int len; | |
413 | char *realm; | |
414 | ||
415 | len = realm_len(context, princ); | |
416 | if (len > REALM_SZ-1) | |
417 | len = REALM_SZ-1; | |
418 | ||
419 | realm = malloc(sizeof(char) * (len+1)); | |
420 | if (realm == NULL) | |
421 | return NULL; | |
422 | ||
423 | strncpy(realm, realm_data(context, princ), len); | |
424 | realm[len] = '\0'; | |
425 | ||
426 | return realm; | |
427 | } | |
428 | ||
429 | static int | |
430 | get_realm_from_cred(krb5_context context, krb5_creds *v5cred, char **realm) { | |
431 | #if !defined(HEIMDAL) && defined(HAVE_KRB5_DECODE_TICKET) | |
432 | krb5_error_code code; | |
433 | krb5_ticket *ticket; | |
434 | ||
435 | *realm = NULL; | |
436 | ||
437 | code = krb5_decode_ticket(&v5cred->ticket, &ticket); | |
438 | if (code) | |
439 | return code; | |
440 | ||
441 | *realm = extract_realm(context, ticket->server); | |
442 | if (*realm == NULL) | |
443 | code = ENOMEM; | |
444 | ||
445 | krb5_free_ticket(context, ticket); | |
446 | ||
447 | return code; | |
448 | #else | |
449 | *realm = NULL; | |
450 | return 0; | |
451 | #endif | |
452 | } | |
453 | ||
454 | /*! | |
455 | * Get a Kerberos service ticket to use as the base of an rxkad token for | |
456 | * a given AFS cell. | |
457 | * | |
458 | * @param[in] context | |
459 | * An initialized Kerberos v5 context | |
460 | * @param[in] realm | |
461 | * The realm to look in for the service principal. If NULL, then the | |
462 | * realm is determined from the cell name or the user's credentials | |
463 | * (see below for the heuristics used) | |
464 | * @param[in] cell | |
465 | * The cell information for the cell to obtain a ticket for | |
466 | * @param[out] v5cred | |
467 | * A Kerberos credentials structure containing the ticket acquired | |
468 | * for the cell. This is a dynamically allocated structure, which | |
469 | * should be freed by using the appropriate Kerberos API function. | |
470 | * @param[out] realmUsed | |
471 | * The realm in which the cell's service principal was located. If | |
472 | * unset, then the principal was located in the same realm as the | |
473 | * current user. This is a malloc'd string which should be freed | |
474 | * by the caller. | |
475 | * | |
476 | * @returns | |
477 | * 0 on success, an error value upon failure | |
478 | * | |
479 | * @notes | |
480 | * This code tries principals in the following, much debated, | |
481 | * order: | |
482 | * | |
483 | * If the realm is specified on the command line we do | |
484 | * - afs/cell@COMMAND-LINE-REALM | |
485 | * - afs@COMMAND-LINE-REALM | |
486 | * | |
487 | * Otherwise, we do | |
488 | * - afs/cell@REALM-FROM-USERS-PRINCIPAL | |
489 | * - afs/cell@krb5_get_host_realm(db-server) | |
490 | * Then, if krb5_get_host_realm(db-server) is non-empty | |
491 | * - afs@ krb5_get_host_realm(db-server) | |
492 | * Otherwise | |
493 | * - afs/cell@ upper-case-domain-of-db-server | |
494 | * - afs@ upper-case-domain-of-db-server | |
495 | * | |
496 | * In all cases, the 'afs@' variant is only tried where the | |
497 | * cell and the realm match case-insensitively. | |
498 | */ | |
499 | ||
500 | static int | |
501 | rxkad_get_ticket(krb5_context context, char *realm, | |
502 | struct afsconf_cell *cell, | |
503 | krb5_creds **v5cred, char **realmUsed) { | |
504 | char *realm_of_cell = NULL; | |
505 | char *realm_of_user = NULL; | |
506 | char *realm_from_princ = NULL; | |
507 | int status; | |
508 | int retry; | |
509 | ||
510 | *realmUsed = NULL; | |
511 | ||
512 | if ((status = get_user_realm(context, &realm_of_user))) { | |
513 | fprintf(stderr, "%s: Couldn't determine realm of user:", progname); | |
514 | afs_com_err(progname, status, " while getting realm"); | |
515 | status = AKLOG_KERBEROS; | |
516 | goto out; | |
517 | } | |
518 | ||
519 | retry = 1; | |
520 | ||
521 | while(retry) { | |
522 | /* Cell on command line - use that one */ | |
523 | if (realm && realm[0]) { | |
524 | realm_of_cell = realm; | |
525 | status = AKLOG_TRYAGAIN; | |
526 | afs_dprintf("We were told to authenticate to realm %s.\n", realm); | |
527 | } else { | |
528 | /* Initially, try using afs/cell@USERREALM */ | |
529 | afs_dprintf("Trying to authenticate to user's realm %s.\n", | |
530 | realm_of_user); | |
531 | realm_of_cell = realm_of_user; | |
532 | status = get_credv5(context, AFSKEY, cell->name, realm_of_cell, | |
533 | v5cred); | |
534 | ||
535 | /* If that failed, try to determine the realm from the name of | |
536 | * one of the DB servers */ | |
537 | if (TRYAGAIN(status)) { | |
538 | realm_of_cell = afs_realm_of_cell(context, cell, FALSE); | |
539 | if (!realm_of_cell) { | |
540 | fprintf(stderr, "%s: Couldn't figure out realm for cell " | |
541 | "%s.\n", progname, cell->name); | |
542 | exit(AKLOG_MISC); | |
543 | } | |
544 | ||
545 | if (realm_of_cell[0]) | |
546 | afs_dprintf("We've deduced that we need to authenticate" | |
547 | " to realm %s.\n", realm_of_cell); | |
548 | else | |
549 | afs_dprintf("We've deduced that we need to authenticate " | |
550 | "using referrals.\n"); | |
551 | } | |
552 | } | |
553 | ||
554 | if (TRYAGAIN(status)) { | |
555 | /* If we've got the full-princ-first option, or we're in a | |
556 | * different realm from the cell - use the cell name as the | |
557 | * instance */ | |
558 | if (AFS_TRY_FULL_PRINC || | |
559 | strcasecmp(cell->name, realm_of_cell)!=0) { | |
560 | status = get_credv5(context, AFSKEY, cell->name, | |
561 | realm_of_cell, v5cred); | |
562 | ||
563 | /* If we failed & we've got an empty realm, then try | |
564 | * calling afs_realm_for_cell again. */ | |
565 | if (TRYAGAIN(status) && !realm_of_cell[0]) { | |
566 | /* This time, get the realm by taking the domain | |
567 | * component of the db server and make it upper case */ | |
568 | realm_of_cell = afs_realm_of_cell(context, cell, TRUE); | |
569 | if (!realm_of_cell) { | |
570 | fprintf(stderr, | |
571 | "%s: Couldn't figure out realm for cell %s.\n", | |
572 | progname, cell->name); | |
573 | exit(AKLOG_MISC); | |
574 | } | |
575 | afs_dprintf("We've deduced that we need to authenticate" | |
576 | " to realm %s.\n", realm_of_cell); | |
577 | status = get_credv5(context, AFSKEY, cell->name, | |
578 | realm_of_cell, v5cred); | |
579 | } | |
580 | } | |
581 | ||
582 | /* If the realm and cell name match, then try without an | |
583 | * instance, but only if realm is non-empty */ | |
584 | ||
585 | if (TRYAGAIN(status) && | |
586 | strcasecmp(cell->name, realm_of_cell) == 0) { | |
587 | status = get_credv5(context, AFSKEY, NULL, realm_of_cell, | |
588 | v5cred); | |
589 | if (!AFS_TRY_FULL_PRINC && TRYAGAIN(status)) { | |
590 | status = get_credv5(context, AFSKEY, cell->name, | |
591 | realm_of_cell, v5cred); | |
592 | } | |
593 | } | |
594 | } | |
595 | ||
596 | /* Try to find a service principal for this cell. | |
597 | * Some broken MIT libraries return KRB5KRB_AP_ERR_MSG_TYPE upon | |
598 | * the first attempt, so we try twice to be sure */ | |
599 | ||
600 | if (status == KRB5KRB_AP_ERR_MSG_TYPE && retry == 1) | |
601 | retry++; | |
602 | else | |
603 | retry = 0; | |
604 | } | |
605 | ||
606 | if (status != 0) { | |
607 | afs_dprintf("Kerberos error code returned by get_cred : %d\n", status); | |
608 | fprintf(stderr, "%s: Couldn't get %s AFS tickets:\n", | |
609 | progname, cell->name); | |
610 | afs_com_err(progname, status, "while getting AFS tickets"); | |
611 | #ifdef KRB5_CC_NOT_KTYPE | |
612 | if (status == KRB5_CC_NOT_KTYPE) { | |
613 | fprintf(stderr, "allow_weak_crypto may be required in the Kerberos configuration\n"); | |
614 | } | |
615 | #endif | |
616 | status = AKLOG_KERBEROS; | |
617 | goto out; | |
618 | } | |
619 | ||
620 | /* If we've got a valid ticket, and we still don't know the realm name | |
621 | * try to figure it out from the contents of the ticket | |
622 | */ | |
623 | if (strcmp(realm_of_cell, "") == 0) { | |
624 | status = get_realm_from_cred(context, *v5cred, &realm_from_princ); | |
625 | if (status) { | |
626 | fprintf(stderr, | |
627 | "%s: Couldn't decode ticket to determine realm for " | |
628 | "cell %s.\n", | |
629 | progname, cell->name); | |
630 | } else { | |
631 | if (realm_from_princ) | |
632 | realm_of_cell = realm_from_princ; | |
633 | } | |
634 | } | |
635 | ||
636 | /* If the realm of the user and cell differ, then we need to use the | |
637 | * realm when we later construct the user's principal */ | |
638 | if (realm_of_cell != NULL && strcmp(realm_of_user, realm_of_cell) != 0) | |
639 | *realmUsed = realm_of_user; | |
640 | ||
641 | out: | |
642 | if (realm_from_princ) | |
643 | free(realm_from_princ); | |
644 | if (realm_of_user && *realmUsed == NULL) | |
645 | free(realm_of_user); | |
646 | ||
647 | return status; | |
648 | } | |
649 | ||
650 | /*! | |
651 | * Build an rxkad token from a Kerberos ticket, using only local tools (that | |
652 | * is, without using a 524 conversion service) | |
653 | * | |
654 | * @param[in] context | |
655 | * An initialised Kerberos 5 context | |
656 | * @param[in] v5cred | |
657 | * A Kerberos credentials structure containing a suitable service ticket | |
658 | * @param[out] tokenPtr | |
659 | * An AFS token structure containing an rxkad token. This is a malloc'd | |
660 | * structure which should be freed by the caller. | |
661 | * @param[out[ userPtr | |
662 | * A string containing the principal of the user to whom the token was | |
663 | * issued. This is a malloc'd block which should be freed by the caller, | |
664 | * if set. | |
665 | * | |
666 | * @returns | |
667 | * 0 on success, an error value upon failure | |
668 | */ | |
669 | static int | |
670 | rxkad_build_native_token(krb5_context context, krb5_creds *v5cred, | |
671 | struct ktc_tokenUnion **tokenPtr, char **userPtr) { | |
672 | char username[BUFSIZ]=""; | |
673 | struct ktc_token token; | |
674 | int status; | |
675 | #ifdef HAVE_NO_KRB5_524 | |
676 | char *p; | |
677 | int len; | |
678 | #else | |
679 | char k4name[ANAME_SZ]; | |
680 | char k4inst[INST_SZ]; | |
681 | char k4realm[REALM_SZ]; | |
682 | #endif | |
683 | void *inkey = get_cred_keydata(v5cred); | |
684 | size_t inkey_sz = get_cred_keylen(v5cred); | |
685 | ||
686 | afs_dprintf("Using Kerberos V5 ticket natively\n"); | |
687 | ||
688 | *tokenPtr = NULL; | |
689 | *userPtr = NULL; | |
690 | ||
691 | #ifndef HAVE_NO_KRB5_524 | |
692 | status = krb5_524_conv_principal (context, v5cred->client, | |
693 | (char *) &k4name, | |
694 | (char *) &k4inst, | |
695 | (char *) &k4realm); | |
696 | if (status) { | |
697 | if (!noprdb) | |
698 | afs_com_err(progname, status, | |
699 | "while converting principal to Kerberos V4 format"); | |
700 | } else { | |
701 | strcpy (username, k4name); | |
702 | if (k4inst[0]) { | |
703 | strcat (username, "."); | |
704 | strcat (username, k4inst); | |
705 | } | |
706 | } | |
707 | #else | |
708 | len = min(get_princ_len(context, v5cred->client, 0), | |
709 | second_comp(context, v5cred->client) ? | |
710 | MAXKTCNAMELEN - 2 : MAXKTCNAMELEN - 1); | |
711 | strncpy(username, get_princ_str(context, v5cred->client, 0), len); | |
712 | username[len] = '\0'; | |
713 | ||
714 | if (second_comp(context, v5cred->client)) { | |
715 | strcat(username, "."); | |
716 | p = username + strlen(username); | |
717 | len = min(get_princ_len(context, v5cred->client, 1), | |
718 | MAXKTCNAMELEN - strlen(username) - 1); | |
719 | strncpy(p, get_princ_str(context, v5cred->client, 1), len); | |
720 | p[len] = '\0'; | |
721 | } | |
722 | #endif | |
723 | ||
724 | memset(&token, 0, sizeof(struct ktc_token)); | |
725 | ||
726 | token.kvno = RXKAD_TKT_TYPE_KERBEROS_V5; | |
727 | token.startTime = v5cred->times.starttime;; | |
728 | token.endTime = v5cred->times.endtime; | |
729 | if (tkt_DeriveDesKey(get_creds_enctype(v5cred), inkey, inkey_sz, | |
730 | &token.sessionKey) != 0) { | |
731 | return RXKADBADKEY; | |
732 | } | |
733 | token.ticketLen = v5cred->ticket.length; | |
734 | memcpy(token.ticket, v5cred->ticket.data, token.ticketLen); | |
735 | ||
736 | status = token_importRxkadViceId(tokenPtr, &token, 0); | |
737 | if (status) { | |
738 | return status; | |
739 | } | |
740 | ||
741 | if (username[0] != '\0') | |
742 | *userPtr = strdup(username); | |
743 | ||
744 | return 0; | |
745 | } | |
746 | ||
747 | /*! | |
748 | * Convert a Keberos ticket to an rxkad token, using information obtained | |
749 | * from an external Kerberos 5->4 conversion service. If the code is built | |
750 | * with HAVE_NO_KRB5_524 then this is a stub function which will always | |
751 | * return success without a token. | |
752 | * | |
753 | * @param[in] context | |
754 | * An initialised Kerberos 5 context | |
755 | * @param[in] v5cred | |
756 | * A Kerberos credentials structure containing a suitable service ticket | |
757 | * @param[out] tokenPtr | |
758 | * An AFS token structure containing an rxkad token. This is a malloc'd | |
759 | * structure which should be freed by the caller. | |
760 | * @param[out[ userPtr | |
761 | * A string containing the principal of the user to whom the token was | |
762 | * issued. This is a malloc'd block which should be freed by the caller, | |
763 | * if set. | |
764 | * | |
765 | * @returns | |
766 | * 0 on success, an error value upon failure | |
767 | */ | |
768 | ||
769 | #ifdef HAVE_NO_KRB5_524 | |
770 | static int | |
771 | rxkad_get_converted_token(krb5_context context, krb5_creds *v5cred, | |
772 | struct ktc_tokenUnion **tokenPtr, char **userPtr) { | |
773 | *tokenPtr = NULL; | |
774 | *userPtr = NULL; | |
775 | ||
776 | return 0; | |
777 | } | |
778 | #else | |
779 | static int | |
780 | rxkad_get_converted_token(krb5_context context, krb5_creds *v5cred, | |
781 | struct ktc_tokenUnion **tokenPtr, char **userPtr) { | |
782 | CREDENTIALS cred; | |
783 | char username[BUFSIZ]; | |
784 | struct ktc_token token; | |
785 | int status; | |
786 | ||
787 | *tokenPtr = NULL; | |
788 | *userPtr = NULL; | |
789 | ||
790 | afs_dprintf("Using Kerberos 524 translator service\n"); | |
791 | ||
792 | status = krb5_524_convert_creds(context, v5cred, &cred); | |
793 | ||
794 | if (status) { | |
795 | afs_com_err(progname, status, "while converting tickets " | |
796 | "to Kerberos V4 format"); | |
797 | return AKLOG_KERBEROS; | |
798 | } | |
799 | ||
800 | strcpy (username, cred.pname); | |
801 | if (cred.pinst[0]) { | |
802 | strcat (username, "."); | |
803 | strcat (username, cred.pinst); | |
804 | } | |
805 | ||
806 | memset(&token, 0, sizeof(struct ktc_token)); | |
807 | ||
808 | token.kvno = cred.kvno; | |
809 | token.startTime = cred.issue_date; | |
810 | /* | |
811 | * It seems silly to go through a bunch of contortions to | |
812 | * extract the expiration time, when the v5 credentials already | |
813 | * has the exact time! Let's use that instead. | |
814 | * | |
815 | * Note that this isn't a security hole, as the expiration time | |
816 | * is also contained in the encrypted token | |
817 | */ | |
818 | token.endTime = v5cred->times.endtime; | |
819 | memcpy(&token.sessionKey, cred.session, 8); | |
820 | token.ticketLen = cred.ticket_st.length; | |
821 | memcpy(token.ticket, cred.ticket_st.dat, token.ticketLen); | |
822 | ||
823 | status = token_importRxkadViceId(tokenPtr, &token, 0); | |
824 | if (status) { | |
825 | return status; | |
826 | } | |
827 | ||
828 | *userPtr = strdup(username); | |
829 | ||
830 | return 0; | |
831 | } | |
832 | #endif | |
833 | ||
834 | /*! | |
835 | * This function gets an rxkad token for a given cell. | |
836 | * | |
837 | * @param[in] context | |
838 | * An initialized Kerberos v5 context | |
839 | * @param[in] cell | |
840 | * The cell information for the cell which we're obtaining a token for | |
841 | * @param[in] realm | |
842 | * The realm to look in for the service principal. If NULL, then the | |
843 | * realm is determined from the cell name or the user's credentials | |
844 | * (see the documentation for rxkad_get_ticket) | |
845 | * @param[out] token | |
846 | * The rxkad token produced. This is a malloc'd structure which should | |
847 | * be freed by the caller. | |
848 | * @parma[out] authuser | |
849 | * A string containing the principal of the user to whom the token was | |
850 | * issued. This is a malloc'd block which should be freed by the caller, | |
851 | * if set. | |
852 | * @param[out] foreign | |
853 | * Whether the user is considered as 'foreign' to the realm of the cell. | |
854 | * | |
855 | * @returns | |
856 | * 0 on success, an error value upon failuer | |
857 | */ | |
858 | static int | |
859 | rxkad_get_token(krb5_context context, struct afsconf_cell *cell, char *realm, | |
860 | struct ktc_tokenUnion **token, char **authuser, int *foreign) { | |
861 | krb5_creds *v5cred; | |
862 | char *realmUsed = NULL; | |
863 | char *username = NULL; | |
864 | int status; | |
865 | ||
866 | *token = NULL; | |
867 | *authuser = NULL; | |
868 | *foreign = 0; | |
869 | ||
870 | status = rxkad_get_ticket(context, realm, cell, &v5cred, &realmUsed); | |
871 | if (status) | |
872 | return status; | |
873 | ||
874 | if (do524) | |
875 | status = rxkad_get_converted_token(context, v5cred, token, &username); | |
876 | else | |
877 | status = rxkad_build_native_token(context, v5cred, token, &username); | |
878 | ||
879 | if (status) | |
880 | goto out; | |
881 | ||
882 | /* We now have the username, plus the realm name, so stitch them together | |
883 | * to give us the name that the ptserver will know the user by */ | |
884 | if (realmUsed == NULL || username == NULL) { | |
885 | *authuser = username; | |
886 | username = NULL; | |
887 | *foreign = 0; | |
888 | } else { | |
889 | if (asprintf(authuser, "%s@%s", username, realmUsed) < 0) { | |
890 | fprintf(stderr, "%s: Out of memory building PTS name\n", progname); | |
891 | *authuser = NULL; | |
892 | status = AKLOG_MISC; | |
893 | goto out; | |
894 | } | |
895 | *foreign = 1; | |
896 | } | |
897 | ||
898 | out: | |
899 | if (realmUsed) | |
900 | free(realmUsed); | |
901 | if (username) | |
902 | free(username); | |
903 | ||
904 | return status; | |
905 | } | |
906 | ||
907 | /* | |
908 | * Log to a cell. If the cell has already been logged to, return without | |
909 | * doing anything. Otherwise, log to it and mark that it has been logged | |
910 | * to. | |
911 | */ | |
912 | static int | |
913 | auth_to_cell(krb5_context context, const char *config, | |
914 | char *cell, char *realm, char **linkedcell) | |
915 | { | |
916 | int status = AKLOG_SUCCESS; | |
917 | int isForeign = 0; | |
918 | char *username = NULL; /* To hold client username structure */ | |
919 | afs_int32 viceId; /* AFS uid of user */ | |
920 | ||
921 | char *local_cell = NULL; | |
922 | struct ktc_tokenUnion *rxkadToken = NULL; | |
923 | struct ktc_setTokenData *token; | |
924 | struct ktc_setTokenData *btoken = NULL; | |
925 | struct afsconf_cell cellconf; | |
926 | ||
927 | /* NULL or empty cell returns information on local cell */ | |
928 | if ((status = get_cellconfig(config, cell, &cellconf, &local_cell))) | |
929 | return(status); | |
930 | ||
931 | if (linkedcell != NULL) { | |
932 | if (cellconf.linkedCell != NULL) { | |
933 | *linkedcell = strdup(cellconf.linkedCell); | |
934 | if (*linkedcell == NULL) { | |
935 | status = ENOMEM; | |
936 | goto out; | |
937 | } | |
938 | } else { | |
939 | *linkedcell = NULL; | |
940 | } | |
941 | } | |
942 | ||
943 | if (ll_string(&authedcells, ll_s_check, cellconf.name)) { | |
944 | afs_dprintf("Already authenticated to %s (or tried to)\n", cellconf.name); | |
945 | status = AKLOG_SUCCESS; | |
946 | goto out; | |
947 | } | |
948 | ||
949 | /* | |
950 | * Record that we have attempted to log to this cell. We do this | |
951 | * before we try rather than after so that we will not try | |
952 | * and fail repeatedly for one cell. | |
953 | */ | |
954 | ll_string(&authedcells, ll_s_add, cellconf.name); | |
955 | ||
956 | /* | |
957 | * Record this cell in the list of zephyr subscriptions. We may | |
958 | * want zephyr subscriptions even if authentication fails. | |
959 | * If this is done after we attempt to get tokens, aklog -zsubs | |
960 | * can return something different depending on whether or not we | |
961 | * are in -noauth mode. | |
962 | */ | |
963 | if (ll_string(&zsublist, ll_s_add, cellconf.name) == LL_FAILURE) { | |
964 | fprintf(stderr, | |
965 | "%s: failure adding cell %s to zephyr subscriptions list.\n", | |
966 | progname, cellconf.name); | |
967 | exit(AKLOG_MISC); | |
968 | } | |
969 | if (ll_string(&zsublist, ll_s_add, local_cell) == LL_FAILURE) { | |
970 | fprintf(stderr, | |
971 | "%s: failure adding cell %s to zephyr subscriptions list.\n", | |
972 | progname, local_cell); | |
973 | exit(AKLOG_MISC); | |
974 | } | |
975 | ||
976 | if (!noauth) { | |
977 | afs_dprintf("Authenticating to cell %s (server %s).\n", cellconf.name, | |
978 | cellconf.hostName[0]); | |
979 | ||
980 | token = token_buildTokenJar(cellconf.name); | |
981 | if (token == NULL) { | |
982 | status = ENOMEM; | |
983 | goto out; | |
984 | } | |
985 | ||
986 | status = rxkad_get_token(context, &cellconf, realm, &rxkadToken, | |
987 | &username, &isForeign); | |
988 | if (status) | |
989 | goto out; | |
990 | ||
991 | /* We need to keep the token structure around so that we can stick | |
992 | * the viceId into it (once we know it) */ | |
993 | status = token_addToken(token, rxkadToken); | |
994 | if (status) { | |
995 | afs_dprintf("Add Token failed with %d", status); | |
996 | goto out; | |
997 | } | |
998 | ||
999 | if (!force && | |
1000 | ktc_GetTokenEx(cellconf.name, &btoken) == 0 && | |
1001 | token_SetsEquivalent(token, btoken)) { | |
1002 | ||
1003 | token_FreeSet(&btoken); | |
1004 | afs_dprintf("Identical tokens already exist; skipping.\n"); | |
1005 | status = AKLOG_SUCCESS; | |
1006 | goto out; | |
1007 | } | |
1008 | ||
1009 | if (btoken) | |
1010 | token_FreeSet(&btoken); | |
1011 | ||
1012 | #ifdef FORCE_NOPRDB | |
1013 | noprdb = 1; | |
1014 | #endif | |
1015 | ||
1016 | if (username == NULL) { | |
1017 | afs_dprintf("Not resolving name to id\n"); | |
1018 | } | |
1019 | else if (noprdb) { | |
1020 | afs_dprintf("Not resolving name %s to id (-noprdb set)\n", username); | |
1021 | } | |
1022 | else { | |
1023 | afs_dprintf("About to resolve name %s to id in cell %s.\n", username, | |
1024 | cellconf.name); | |
1025 | ||
1026 | if (!pr_Initialize (0, AFSDIR_CLIENT_ETC_DIRPATH, cellconf.name)) | |
1027 | status = pr_SNameToId (username, &viceId); | |
1028 | ||
1029 | if (status) | |
1030 | afs_dprintf("Error %d\n", status); | |
1031 | else | |
1032 | afs_dprintf("Id %d\n", (int) viceId); | |
1033 | ||
1034 | ||
1035 | /* | |
1036 | * This code is taken from cklog -- it lets people | |
1037 | * automatically register with the ptserver in foreign cells | |
1038 | */ | |
1039 | ||
1040 | #ifdef ALLOW_REGISTER | |
1041 | if ((status == 0) && (viceId == ANONYMOUSID) && isForeign) { | |
1042 | afs_dprintf("doing first-time registration of %s at %s\n", | |
1043 | username, cellconf.name); | |
1044 | viceId = 0; | |
1045 | ||
1046 | status = ktc_SetTokenEx(token); | |
1047 | if (status) { | |
1048 | afs_com_err(progname, status, | |
1049 | "while obtaining tokens for cell %s", | |
1050 | cellconf.name); | |
1051 | status = AKLOG_TOKEN; | |
1052 | } | |
1053 | ||
1054 | /* | |
1055 | * In case you're wondering, we don't need to change the | |
1056 | * filename here because we're still connecting to the | |
1057 | * same cell -- we're just using a different authenticat ion | |
1058 | * level | |
1059 | */ | |
1060 | ||
1061 | if ((status = pr_Initialize(1L, AFSDIR_CLIENT_ETC_DIRPATH, | |
1062 | cellconf.name))) { | |
1063 | printf("Error %d\n", status); | |
1064 | } | |
1065 | ||
1066 | if ((status = pr_CreateUser(username, &viceId))) { | |
1067 | fprintf(stderr, "%s: %s so unable to create remote PTS " | |
1068 | "user %s in cell %s (status: %d).\n", progname, | |
1069 | afs_error_message(status), username, cellconf.name, | |
1070 | status); | |
1071 | viceId = ANONYMOUSID; | |
1072 | } else { | |
1073 | printf("created cross-cell entry for %s (Id %d) at %s\n", | |
1074 | username, viceId, cellconf.name); | |
1075 | } | |
1076 | } | |
1077 | #endif /* ALLOW_REGISTER */ | |
1078 | ||
1079 | if ((status == 0) && (viceId != ANONYMOUSID)) { | |
1080 | status = token_setRxkadViceId(rxkadToken, viceId); | |
1081 | if (status) { | |
1082 | fprintf(stderr, "Error %d setting rxkad ViceId\n", status); | |
1083 | status = AKLOG_SUCCESS; | |
1084 | } else { | |
1085 | token_replaceToken(token, rxkadToken); | |
1086 | } | |
1087 | } | |
1088 | } | |
1089 | ||
1090 | if (username) { | |
1091 | afs_dprintf("Setting tokens. %s @ %s\n", | |
1092 | username, cellconf.name); | |
1093 | } else { | |
1094 | afs_dprintf("Setting tokens for cell %s\n", cellconf.name); | |
1095 | } | |
1096 | ||
1097 | #ifndef AFS_AIX51_ENV | |
1098 | /* on AIX 4.1.4 with AFS 3.4a+ if a write is not done before | |
1099 | * this routine, it will not add the token. It is not clear what | |
1100 | * is going on here! So we will do the following operation. | |
1101 | * On AIX 5, it causes the parent program to die, so we won't. | |
1102 | * We don't care about the return value, but need to collect it | |
1103 | * to avoid compiler warnings. | |
1104 | */ | |
1105 | if (write(2,"",0) < 0) { | |
1106 | /* dummy write, don't care */ | |
1107 | } | |
1108 | #endif | |
1109 | token_setPag(token, afssetpag); | |
1110 | status = ktc_SetTokenEx(token); | |
1111 | if (status) { | |
1112 | afs_com_err(progname, status, "while setting tokens for cell %s", | |
1113 | cellconf.name); | |
1114 | status = AKLOG_TOKEN; | |
1115 | } | |
1116 | } | |
1117 | else | |
1118 | afs_dprintf("Noauth mode; not authenticating.\n"); | |
1119 | ||
1120 | out: | |
1121 | if (rxkadToken) { | |
1122 | token_freeToken(&rxkadToken); | |
1123 | } | |
1124 | ||
1125 | if (local_cell) | |
1126 | free(local_cell); | |
1127 | if (username) | |
1128 | free(username); | |
1129 | ||
1130 | return(status); | |
1131 | } | |
1132 | ||
1133 | static int | |
1134 | get_afs_mountpoint(char *file, char *mountpoint, int size) | |
1135 | { | |
1136 | #ifdef AFS_SUN_ENV | |
1137 | char V ='V'; /* AFS has problem on Sun with pioctl */ | |
1138 | #endif | |
1139 | char our_file[MAXPATHLEN + 1]; | |
1140 | char *parent_dir; | |
1141 | char *last_component; | |
1142 | struct ViceIoctl vio; | |
1143 | char cellname[BUFSIZ]; | |
1144 | ||
1145 | strlcpy(our_file, file, sizeof(our_file)); | |
1146 | ||
1147 | if ((last_component = strrchr(our_file, DIR))) { | |
1148 | *last_component++ = 0; | |
1149 | parent_dir = our_file; | |
1150 | } | |
1151 | else { | |
1152 | last_component = our_file; | |
1153 | parent_dir = "."; | |
1154 | } | |
1155 | ||
1156 | memset(cellname, 0, sizeof(cellname)); | |
1157 | ||
1158 | vio.in = last_component; | |
1159 | vio.in_size = strlen(last_component)+1; | |
1160 | vio.out_size = size; | |
1161 | vio.out = mountpoint; | |
1162 | ||
1163 | if (!pioctl(parent_dir, VIOC_AFS_STAT_MT_PT, &vio, 0)) { | |
1164 | if (strchr(mountpoint, VOLMARKER) == NULL) { | |
1165 | vio.in = file; | |
1166 | vio.in_size = strlen(file) + 1; | |
1167 | vio.out_size = sizeof(cellname); | |
1168 | vio.out = cellname; | |
1169 | ||
1170 | if (!pioctl(file, VIOC_FILE_CELL_NAME, &vio, 1)) { | |
1171 | strlcat(cellname, VOLMARKERSTRING, sizeof(cellname)); | |
1172 | strlcat(cellname, mountpoint + 1, sizeof(cellname)); | |
1173 | memset(mountpoint + 1, 0, size - 1); | |
1174 | strcpy(mountpoint + 1, cellname); | |
1175 | } | |
1176 | } | |
1177 | return(TRUE); | |
1178 | } | |
1179 | else | |
1180 | return(FALSE); | |
1181 | } | |
1182 | ||
1183 | /* | |
1184 | * This routine each time it is called returns the next directory | |
1185 | * down a pathname. It resolves all symbolic links. The first time | |
1186 | * it is called, it should be called with the name of the path | |
1187 | * to be descended. After that, it should be called with the arguemnt | |
1188 | * NULL. | |
1189 | */ | |
1190 | static char * | |
1191 | next_path(char *origpath) | |
1192 | { | |
1193 | static char path[MAXPATHLEN + 1]; | |
1194 | static char pathtocheck[MAXPATHLEN + 1]; | |
1195 | ||
1196 | ssize_t link; /* Return value from readlink */ | |
1197 | char linkbuf[MAXPATHLEN + 1]; | |
1198 | char tmpbuf[MAXPATHLEN + 1]; | |
1199 | ||
1200 | static char *last_comp; /* last component of directory name */ | |
1201 | static char *elast_comp; /* End of last component */ | |
1202 | char *t; | |
1203 | int len; | |
1204 | ||
1205 | static int symlinkcount = 0; /* We can't exceed MAXSYMLINKS */ | |
1206 | ||
1207 | /* If we are given something for origpath, we are initializing only. */ | |
1208 | if (origpath) { | |
1209 | memset(path, 0, sizeof(path)); | |
1210 | memset(pathtocheck, 0, sizeof(pathtocheck)); | |
1211 | strlcpy(path, origpath, sizeof(path)); | |
1212 | last_comp = path; | |
1213 | symlinkcount = 0; | |
1214 | return(NULL); | |
1215 | } | |
1216 | ||
1217 | /* We were not given origpath; find then next path to check */ | |
1218 | ||
1219 | /* If we've gotten all the way through already, return NULL */ | |
1220 | if (last_comp == NULL) | |
1221 | return(NULL); | |
1222 | ||
1223 | do { | |
1224 | while (*last_comp == DIR) | |
1225 | strncat(pathtocheck, last_comp++, 1); | |
1226 | len = (elast_comp = strchr(last_comp, DIR)) | |
1227 | ? elast_comp - last_comp : strlen(last_comp); | |
1228 | strncat(pathtocheck, last_comp, len); | |
1229 | memset(linkbuf, 0, sizeof(linkbuf)); | |
1230 | link = readlink(pathtocheck, linkbuf, sizeof(linkbuf)-1); | |
1231 | ||
1232 | if (link > 0) { | |
1233 | linkbuf[link] = '\0'; /* NUL terminate string */ | |
1234 | ||
1235 | if (++symlinkcount > MAXSYMLINKS) { | |
1236 | fprintf(stderr, "%s: %s\n", progname, strerror(ELOOP)); | |
1237 | exit(AKLOG_BADPATH); | |
1238 | } | |
1239 | ||
1240 | memset(tmpbuf, 0, sizeof(tmpbuf)); | |
1241 | if (elast_comp) | |
1242 | strlcpy(tmpbuf, elast_comp, sizeof(tmpbuf)); | |
1243 | if (linkbuf[0] == DIR) { | |
1244 | /* | |
1245 | * If this is a symbolic link to an absolute path, | |
1246 | * replace what we have by the absolute path. | |
1247 | */ | |
1248 | memset(path, 0, strlen(path)); | |
1249 | memcpy(path, linkbuf, sizeof(linkbuf)); | |
1250 | strcat(path, tmpbuf); | |
1251 | last_comp = path; | |
1252 | elast_comp = NULL; | |
1253 | memset(pathtocheck, 0, sizeof(pathtocheck)); | |
1254 | } | |
1255 | else { | |
1256 | /* | |
1257 | * If this is a symbolic link to a relative path, | |
1258 | * replace only the last component with the link name. | |
1259 | */ | |
1260 | strncpy(last_comp, linkbuf, strlen(linkbuf) + 1); | |
1261 | strcat(path, tmpbuf); | |
1262 | elast_comp = NULL; | |
1263 | if ((t = strrchr(pathtocheck, DIR))) { | |
1264 | t++; | |
1265 | memset(t, 0, strlen(t)); | |
1266 | } | |
1267 | else | |
1268 | memset(pathtocheck, 0, sizeof(pathtocheck)); | |
1269 | } | |
1270 | } | |
1271 | else | |
1272 | last_comp = elast_comp; | |
1273 | } | |
1274 | while(link > 0); | |
1275 | ||
1276 | return(pathtocheck); | |
1277 | } | |
1278 | ||
1279 | static void | |
1280 | add_hosts(char *file) | |
1281 | { | |
1282 | #ifdef AFS_SUN_ENV | |
1283 | char V = 'V'; /* AFS has problem on SunOS */ | |
1284 | #endif | |
1285 | struct ViceIoctl vio; | |
1286 | char outbuf[BUFSIZ]; | |
1287 | long *phosts; | |
1288 | int i; | |
1289 | struct hostent *hp; | |
1290 | struct in_addr in; | |
1291 | ||
1292 | memset(outbuf, 0, sizeof(outbuf)); | |
1293 | ||
1294 | vio.out_size = sizeof(outbuf); | |
1295 | vio.in_size = 0; | |
1296 | vio.out = outbuf; | |
1297 | ||
1298 | afs_dprintf("Getting list of hosts for %s\n", file); | |
1299 | ||
1300 | /* Don't worry about errors. */ | |
1301 | if (!pioctl(file, VIOCWHEREIS, &vio, 1)) { | |
1302 | phosts = (long *) outbuf; | |
1303 | ||
1304 | /* | |
1305 | * Lists hosts that we care about. If ALLHOSTS is defined, | |
1306 | * then all hosts that you ever may possible go through are | |
1307 | * included in this list. If not, then only hosts that are | |
1308 | * the only ones appear. That is, if a volume you must use | |
1309 | * is replaced on only one server, that server is included. | |
1310 | * If it is replicated on many servers, then none are included. | |
1311 | * This is not perfect, but the result is that people don't | |
1312 | * get subscribed to a lot of instances of FILSRV that they | |
1313 | * probably won't need which reduces the instances of | |
1314 | * people getting messages that don't apply to them. | |
1315 | */ | |
1316 | #ifndef ALLHOSTS | |
1317 | if (phosts[1] != '\0') | |
1318 | return; | |
1319 | #endif | |
1320 | for (i = 0; phosts[i]; i++) { | |
1321 | if (hosts) { | |
1322 | in.s_addr = phosts[i]; | |
1323 | afs_dprintf("Got host %s\n", inet_ntoa(in)); | |
1324 | ll_string(&hostlist, ll_s_add, (char *)inet_ntoa(in)); | |
1325 | } | |
1326 | if (zsubs && (hp=gethostbyaddr((char *) &phosts[i],sizeof(long),AF_INET))) { | |
1327 | afs_dprintf("Got host %s\n", hp->h_name); | |
1328 | ll_string(&zsublist, ll_s_add, hp->h_name); | |
1329 | } | |
1330 | } | |
1331 | } | |
1332 | } | |
1333 | ||
1334 | /* | |
1335 | * This routine descends through a path to a directory, logging to | |
1336 | * every cell it encounters along the way. | |
1337 | */ | |
1338 | static int | |
1339 | auth_to_path(krb5_context context, const char *config, char *path) | |
1340 | { | |
1341 | int status = AKLOG_SUCCESS; | |
1342 | int auth_status = AKLOG_SUCCESS; | |
1343 | ||
1344 | char *nextpath; | |
1345 | char pathtocheck[MAXPATHLEN + 1]; | |
1346 | char mountpoint[MAXPATHLEN + 1]; | |
1347 | ||
1348 | char *cell; | |
1349 | char *endofcell; | |
1350 | ||
1351 | u_char isdirectory; | |
1352 | ||
1353 | /* Initialize */ | |
1354 | if (path[0] == DIR) | |
1355 | strlcpy(pathtocheck, path, sizeof(pathtocheck)); | |
1356 | else { | |
1357 | if (getcwd(pathtocheck, sizeof(pathtocheck)) == NULL) { | |
1358 | fprintf(stderr, "Unable to find current working directory:\n"); | |
1359 | fprintf(stderr, "%s\n", pathtocheck); | |
1360 | fprintf(stderr, "Try an absolute pathname.\n"); | |
1361 | exit(AKLOG_BADPATH); | |
1362 | } | |
1363 | else { | |
1364 | strlcat(pathtocheck, DIRSTRING, sizeof(pathtocheck)); | |
1365 | strlcat(pathtocheck, path, sizeof(pathtocheck)); | |
1366 | } | |
1367 | } | |
1368 | next_path(pathtocheck); | |
1369 | ||
1370 | /* Go on to the next level down the path */ | |
1371 | while ((nextpath = next_path(NULL))) { | |
1372 | strlcpy(pathtocheck, nextpath, sizeof(pathtocheck)); | |
1373 | afs_dprintf("Checking directory %s\n", pathtocheck); | |
1374 | /* | |
1375 | * If this is an afs mountpoint, determine what cell from | |
1376 | * the mountpoint name which is of the form | |
1377 | * #cellname:volumename or %cellname:volumename. | |
1378 | */ | |
1379 | if (get_afs_mountpoint(pathtocheck, mountpoint, sizeof(mountpoint))) { | |
1380 | /* skip over the '#' or '%' */ | |
1381 | cell = mountpoint + 1; | |
1382 | /* Add this (cell:volumename) to the list of zsubs */ | |
1383 | if (zsubs) | |
1384 | ll_string(&zsublist, ll_s_add, cell); | |
1385 | if (zsubs || hosts) | |
1386 | add_hosts(pathtocheck); | |
1387 | if ((endofcell = strchr(mountpoint, VOLMARKER))) { | |
1388 | *endofcell = '\0'; | |
1389 | auth_status = auth_to_cell(context, config, cell, NULL, NULL); | |
1390 | if (auth_status) { | |
1391 | if (status == AKLOG_SUCCESS) | |
1392 | status = auth_status; | |
1393 | else if (status != auth_status) | |
1394 | status = AKLOG_SOMETHINGSWRONG; | |
1395 | } | |
1396 | } | |
1397 | } | |
1398 | else { | |
1399 | if (isdir(pathtocheck, &isdirectory) < 0) { | |
1400 | /* | |
1401 | * If we've logged and still can't stat, there's | |
1402 | * a problem... | |
1403 | */ | |
1404 | fprintf(stderr, "%s: stat(%s): %s\n", progname, | |
1405 | pathtocheck, strerror(errno)); | |
1406 | return(AKLOG_BADPATH); | |
1407 | } | |
1408 | else if (! isdirectory) { | |
1409 | /* Allow only directories */ | |
1410 | fprintf(stderr, "%s: %s: %s\n", progname, pathtocheck, | |
1411 | strerror(ENOTDIR)); | |
1412 | return(AKLOG_BADPATH); | |
1413 | } | |
1414 | } | |
1415 | } | |
1416 | ||
1417 | ||
1418 | return(status); | |
1419 | } | |
1420 | ||
1421 | ||
1422 | /* Print usage message and exit */ | |
1423 | static void | |
1424 | usage(void) | |
1425 | { | |
1426 | fprintf(stderr, "\nUsage: %s %s%s%s\n", progname, | |
1427 | "[-d] [[-cell | -c] cell [-k krb_realm]] ", | |
1428 | "[[-p | -path] pathname]\n", | |
1429 | " [-zsubs] [-hosts] [-noauth] [-noprdb] [-force] [-setpag] \n" | |
1430 | " [-linked]" | |
1431 | #ifndef HAVE_NO_KRB5_524 | |
1432 | " [-524]" | |
1433 | #endif | |
1434 | "\n"); | |
1435 | fprintf(stderr, " -d gives debugging information.\n"); | |
1436 | fprintf(stderr, " krb_realm is the kerberos realm of a cell.\n"); | |
1437 | fprintf(stderr, " pathname is the name of a directory to which "); | |
1438 | fprintf(stderr, "you wish to authenticate.\n"); | |
1439 | fprintf(stderr, " -zsubs gives zephyr subscription information.\n"); | |
1440 | fprintf(stderr, " -hosts gives host address information.\n"); | |
1441 | fprintf(stderr, " -noauth does not attempt to get tokens.\n"); | |
1442 | fprintf(stderr, " -noprdb means don't try to determine AFS ID.\n"); | |
1443 | fprintf(stderr, " -force means replace identical tickets. \n"); | |
1444 | fprintf(stderr, " -linked means if AFS node is linked, try both. \n"); | |
1445 | fprintf(stderr, " -setpag set the AFS process authentication group.\n"); | |
1446 | #ifndef HAVE_NO_KRB5_524 | |
1447 | fprintf(stderr, " -524 means use the 524 converter instead of V5 directly\n"); | |
1448 | #endif | |
1449 | fprintf(stderr, " No commandline arguments means "); | |
1450 | fprintf(stderr, "authenticate to the local cell.\n"); | |
1451 | fprintf(stderr, "\n"); | |
1452 | exit(AKLOG_USAGE); | |
1453 | } | |
1454 | ||
1455 | int | |
1456 | main(int argc, char *argv[]) | |
1457 | { | |
1458 | krb5_context context; | |
1459 | int status = AKLOG_SUCCESS; | |
1460 | int i; | |
1461 | int somethingswrong = FALSE; | |
1462 | ||
1463 | cellinfo_t cellinfo; | |
1464 | ||
1465 | extern char *progname; /* Name of this program */ | |
1466 | ||
1467 | int cmode = FALSE; /* Cellname mode */ | |
1468 | int pmode = FALSE; /* Path name mode */ | |
1469 | ||
1470 | char realm[REALM_SZ]; /* Kerberos realm of afs server */ | |
1471 | char cell[BUFSIZ]; /* Cell to which we are authenticating */ | |
1472 | char path[MAXPATHLEN + 1]; /* Path length for path mode */ | |
1473 | ||
1474 | linked_list cells; /* List of cells to log to */ | |
1475 | linked_list paths; /* List of paths to log to */ | |
1476 | ll_node *cur_node; | |
1477 | char *linkedcell; | |
1478 | const char *config = AFSDIR_CLIENT_ETC_DIRPATH; | |
1479 | ||
1480 | memset(&cellinfo, 0, sizeof(cellinfo)); | |
1481 | ||
1482 | memset(realm, 0, sizeof(realm)); | |
1483 | memset(cell, 0, sizeof(cell)); | |
1484 | memset(path, 0, sizeof(path)); | |
1485 | ||
1486 | ll_init(&cells); | |
1487 | ll_init(&paths); | |
1488 | ||
1489 | ll_init(&zsublist); | |
1490 | ll_init(&hostlist); | |
1491 | ||
1492 | /* Store the program name here for error messages */ | |
1493 | if ((progname = strrchr(argv[0], DIR))) | |
1494 | progname++; | |
1495 | else | |
1496 | progname = argv[0]; | |
1497 | ||
1498 | #if defined(KRB5_PROG_ETYPE_NOSUPP) && !(defined(HAVE_KRB5_ENCTYPE_ENABLE) || defined(HAVE_KRB5_ALLOW_WEAK_CRYPTO)) | |
1499 | { | |
1500 | char *filepath = NULL, *newpath = NULL; | |
1501 | #ifndef AFS_DARWIN_ENV | |
1502 | char *defaultpath = "/etc/krb5.conf:/etc/krb5/krb5.conf"; | |
1503 | #else | |
1504 | char *defaultpath = "~/Library/Preferences/edu.mit.Kerberos:/Library/Preferences/edu.mit.Kerberos"; | |
1505 | #endif | |
1506 | filepath = getenv("KRB5_CONFIG"); | |
1507 | ||
1508 | /* only fiddle with KRB5_CONFIG if krb5-weak.conf actually exists */ | |
1509 | if (asprintf(&newpath, "%s/krb5-weak.conf", | |
1510 | AFSDIR_CLIENT_ETC_DIRPATH) < 0) | |
1511 | newpath = NULL; | |
1512 | if (newpath != NULL && access(newpath, R_OK) == 0) { | |
1513 | free(newpath); | |
1514 | newpath = NULL; | |
1515 | if (asprintf(&newpath, "%s:%s/krb5-weak.conf", | |
1516 | filepath ? filepath : defaultpath, | |
1517 | AFSDIR_CLIENT_ETC_DIRPATH) < 0) | |
1518 | newpath = NULL; | |
1519 | else | |
1520 | setenv("KRB5_CONFIG", newpath, 1); | |
1521 | } | |
1522 | #endif | |
1523 | krb5_init_context(&context); | |
1524 | ||
1525 | #if defined(KRB5_PROG_ETYPE_NOSUPP) && !(defined(HAVE_KRB5_ENCTYPE_ENABLE) || defined(HAVE_KRB5_ALLOW_WEAK_CRYPTO)) | |
1526 | if (newpath) | |
1527 | free(newpath); | |
1528 | if (filepath) | |
1529 | setenv("KRB5_CONFIG", filepath, 1); | |
1530 | else | |
1531 | unsetenv("KRB5_CONFIG"); | |
1532 | } | |
1533 | #endif | |
1534 | initialize_KTC_error_table (); | |
1535 | initialize_U_error_table(); | |
1536 | initialize_RXK_error_table(); | |
1537 | initialize_ACFG_error_table(); | |
1538 | initialize_PT_error_table(); | |
1539 | afs_set_com_err_hook(redirect_errors); | |
1540 | ||
1541 | /* | |
1542 | * Enable DES enctypes, which are currently still required for AFS. | |
1543 | * krb5_allow_weak_crypto is MIT Kerberos 1.8. krb5_enctype_enable is | |
1544 | * Heimdal. | |
1545 | */ | |
1546 | #if defined(HAVE_KRB5_ENCTYPE_ENABLE) | |
1547 | i = krb5_enctype_valid(context, ETYPE_DES_CBC_CRC); | |
1548 | if (i) | |
1549 | krb5_enctype_enable(context, ETYPE_DES_CBC_CRC); | |
1550 | #elif defined(HAVE_KRB5_ALLOW_WEAK_CRYPTO) | |
1551 | krb5_allow_weak_crypto(context, 1); | |
1552 | #endif | |
1553 | ||
1554 | /* Initialize list of cells to which we have authenticated */ | |
1555 | ll_init(&authedcells); | |
1556 | ||
1557 | /* Parse commandline arguments and make list of what to do. */ | |
1558 | for (i = 1; i < argc; i++) { | |
1559 | if (strcmp(argv[i], "-d") == 0) | |
1560 | dflag++; | |
1561 | else if (strcmp(argv[i], "-noauth") == 0) | |
1562 | noauth++; | |
1563 | else if (strcmp(argv[i], "-zsubs") == 0) | |
1564 | zsubs++; | |
1565 | else if (strcmp(argv[i], "-hosts") == 0) | |
1566 | hosts++; | |
1567 | else if (strcmp(argv[i], "-noprdb") == 0) | |
1568 | noprdb++; | |
1569 | else if (strcmp(argv[i], "-linked") == 0) | |
1570 | linked++; | |
1571 | else if (strcmp(argv[i], "-force") == 0) | |
1572 | force++; | |
1573 | #ifndef HAVE_NO_KRB5_524 | |
1574 | else if (strcmp(argv[i], "-524") == 0) | |
1575 | do524++; | |
1576 | #endif | |
1577 | else if (strcmp(argv[i], "-setpag") == 0) | |
1578 | afssetpag++; | |
1579 | else if (((strcmp(argv[i], "-cell") == 0) || | |
1580 | (strcmp(argv[i], "-c") == 0)) && !pmode) | |
1581 | if (++i < argc) { | |
1582 | cmode++; | |
1583 | strlcpy(cell, argv[i], sizeof(cell)); | |
1584 | } | |
1585 | else | |
1586 | usage(); | |
1587 | else if ((strcmp(argv[i], "-keytab") == 0)) | |
1588 | if (++i < argc) { | |
1589 | keytab = argv[i]; | |
1590 | } | |
1591 | else | |
1592 | usage(); | |
1593 | else if ((strcmp(argv[i], "-principal") == 0)) | |
1594 | if (++i < argc) { | |
1595 | client = argv[i]; | |
1596 | } | |
1597 | else | |
1598 | usage(); | |
1599 | else if (((strcmp(argv[i], "-path") == 0) || | |
1600 | (strcmp(argv[i], "-p") == 0)) && !cmode) | |
1601 | if (++i < argc) { | |
1602 | pmode++; | |
1603 | strlcpy(path, argv[i], sizeof(path)); | |
1604 | } | |
1605 | else | |
1606 | usage(); | |
1607 | else if (strcmp(argv[i], "-config") == 0) | |
1608 | if (++i < argc) { | |
1609 | config = argv[i]; | |
1610 | } | |
1611 | else | |
1612 | usage(); | |
1613 | else if (argv[i][0] == '-') | |
1614 | usage(); | |
1615 | else if (!pmode && !cmode) { | |
1616 | if (strchr(argv[i], DIR) || (strcmp(argv[i], ".") == 0) || | |
1617 | (strcmp(argv[i], "..") == 0)) { | |
1618 | pmode++; | |
1619 | strlcpy(path, argv[i], sizeof(path)); | |
1620 | } | |
1621 | else { | |
1622 | cmode++; | |
1623 | strlcpy(cell, argv[i], sizeof(cell)); | |
1624 | } | |
1625 | } | |
1626 | else | |
1627 | usage(); | |
1628 | ||
1629 | if (cmode) { | |
1630 | if (((i + 1) < argc) && (strcmp(argv[i + 1], "-k") == 0)) { | |
1631 | i+=2; | |
1632 | if (i < argc) | |
1633 | strlcpy(realm, argv[i], sizeof(realm)); | |
1634 | else | |
1635 | usage(); | |
1636 | } | |
1637 | /* Add this cell to list of cells */ | |
1638 | strcpy(cellinfo.cell, cell); | |
1639 | strcpy(cellinfo.realm, realm); | |
1640 | if ((cur_node = ll_add_node(&cells, ll_tail))) { | |
1641 | char *new_cellinfo; | |
1642 | if ((new_cellinfo = copy_cellinfo(&cellinfo))) | |
1643 | ll_add_data(cur_node, new_cellinfo); | |
1644 | else { | |
1645 | fprintf(stderr, | |
1646 | "%s: failure copying cellinfo.\n", progname); | |
1647 | exit(AKLOG_MISC); | |
1648 | } | |
1649 | } | |
1650 | else { | |
1651 | fprintf(stderr, "%s: failure adding cell to cells list.\n", | |
1652 | progname); | |
1653 | exit(AKLOG_MISC); | |
1654 | } | |
1655 | memset(&cellinfo, 0, sizeof(cellinfo)); | |
1656 | cmode = FALSE; | |
1657 | memset(cell, 0, sizeof(cell)); | |
1658 | memset(realm, 0, sizeof(realm)); | |
1659 | } | |
1660 | else if (pmode) { | |
1661 | /* Add this path to list of paths */ | |
1662 | if ((cur_node = ll_add_node(&paths, ll_tail))) { | |
1663 | char *new_path; | |
1664 | if ((new_path = strdup(path))) | |
1665 | ll_add_data(cur_node, new_path); | |
1666 | else { | |
1667 | fprintf(stderr, "%s: failure copying path name.\n", | |
1668 | progname); | |
1669 | exit(AKLOG_MISC); | |
1670 | } | |
1671 | } | |
1672 | else { | |
1673 | fprintf(stderr, "%s: failure adding path to paths list.\n", | |
1674 | progname); | |
1675 | exit(AKLOG_MISC); | |
1676 | } | |
1677 | pmode = FALSE; | |
1678 | memset(path, 0, sizeof(path)); | |
1679 | } | |
1680 | } | |
1681 | ||
1682 | /* If nothing was given, log to the local cell. */ | |
1683 | if ((cells.nelements + paths.nelements) == 0) { | |
1684 | struct passwd *pwd; | |
1685 | ||
1686 | status = auth_to_cell(context, config, NULL, NULL, &linkedcell); | |
1687 | ||
1688 | /* If this cell is linked to a DCE cell, and user requested -linked, | |
1689 | * get tokens for both. This is very useful when the AFS cell is | |
1690 | * linked to a DFS cell and this system does not also have DFS. | |
1691 | */ | |
1692 | ||
1693 | if (!status && linked && linkedcell != NULL) { | |
1694 | afs_dprintf("Linked cell: %s\n", linkedcell); | |
1695 | status = auth_to_cell(context, config, linkedcell, NULL, NULL); | |
1696 | } | |
1697 | if (linkedcell) { | |
1698 | free(linkedcell); | |
1699 | linkedcell = NULL; | |
1700 | } | |
1701 | ||
1702 | /* | |
1703 | * Local hack - if the person has a file in their home | |
1704 | * directory called ".xlog", read that for a list of | |
1705 | * extra cells to authenticate to | |
1706 | */ | |
1707 | ||
1708 | if ((pwd = getpwuid(getuid())) != NULL) { | |
1709 | struct stat sbuf; | |
1710 | FILE *f; | |
1711 | char fcell[100], xlog_path[512]; | |
1712 | ||
1713 | strlcpy(xlog_path, pwd->pw_dir, sizeof(xlog_path)); | |
1714 | strlcat(xlog_path, "/.xlog", sizeof(xlog_path)); | |
1715 | ||
1716 | if ((stat(xlog_path, &sbuf) == 0) && | |
1717 | ((f = fopen(xlog_path, "r")) != NULL)) { | |
1718 | ||
1719 | afs_dprintf("Reading %s for cells to authenticate to.\n", | |
1720 | xlog_path); | |
1721 | ||
1722 | while (fgets(fcell, 100, f) != NULL) { | |
1723 | int auth_status; | |
1724 | ||
1725 | fcell[strlen(fcell) - 1] = '\0'; | |
1726 | ||
1727 | afs_dprintf("Found cell %s in %s.\n", fcell, xlog_path); | |
1728 | ||
1729 | auth_status = auth_to_cell(context, config, fcell, NULL, NULL); | |
1730 | if (status == AKLOG_SUCCESS) | |
1731 | status = auth_status; | |
1732 | else | |
1733 | status = AKLOG_SOMETHINGSWRONG; | |
1734 | } | |
1735 | } | |
1736 | } | |
1737 | } | |
1738 | else { | |
1739 | /* Log to all cells in the cells list first */ | |
1740 | for (cur_node = cells.first; cur_node; cur_node = cur_node->next) { | |
1741 | memcpy((char *)&cellinfo, cur_node->data, sizeof(cellinfo)); | |
1742 | status = auth_to_cell(context, config, cellinfo.cell, | |
1743 | cellinfo.realm, &linkedcell); | |
1744 | if (status) { | |
1745 | somethingswrong++; | |
1746 | } else { | |
1747 | if (linked && linkedcell != NULL) { | |
1748 | afs_dprintf("Linked cell: %s\n", linkedcell); | |
1749 | status = auth_to_cell(context, config, linkedcell, | |
1750 | cellinfo.realm, NULL); | |
1751 | if (status) | |
1752 | somethingswrong++; | |
1753 | } | |
1754 | if (linkedcell != NULL) { | |
1755 | free(linkedcell); | |
1756 | linkedcell = NULL; | |
1757 | } | |
1758 | } | |
1759 | } | |
1760 | ||
1761 | /* Then, log to all paths in the paths list */ | |
1762 | for (cur_node = paths.first; cur_node; cur_node = cur_node->next) { | |
1763 | status = auth_to_path(context, config, cur_node->data); | |
1764 | if (status) | |
1765 | somethingswrong++; | |
1766 | } | |
1767 | ||
1768 | /* | |
1769 | * If only one thing was logged to, we'll return the status | |
1770 | * of the single call. Otherwise, we'll return a generic | |
1771 | * something failed status. | |
1772 | */ | |
1773 | if (somethingswrong && ((cells.nelements + paths.nelements) > 1)) | |
1774 | status = AKLOG_SOMETHINGSWRONG; | |
1775 | } | |
1776 | ||
1777 | /* If we are keeping track of zephyr subscriptions, print them. */ | |
1778 | if (zsubs) | |
1779 | for (cur_node = zsublist.first; cur_node; cur_node = cur_node->next) { | |
1780 | printf("zsub: %s\n", cur_node->data); | |
1781 | } | |
1782 | ||
1783 | /* If we are keeping track of host information, print it. */ | |
1784 | if (hosts) | |
1785 | for (cur_node = hostlist.first; cur_node; cur_node = cur_node->next) { | |
1786 | printf("host: %s\n", cur_node->data); | |
1787 | } | |
1788 | ||
1789 | exit(status); | |
1790 | } | |
1791 | ||
1792 | static int | |
1793 | isdir(char *path, unsigned char *val) | |
1794 | { | |
1795 | struct stat statbuf; | |
1796 | ||
1797 | if (lstat(path, &statbuf) < 0) | |
1798 | return (-1); | |
1799 | else { | |
1800 | if ((statbuf.st_mode & S_IFMT) == S_IFDIR) | |
1801 | *val = TRUE; | |
1802 | else | |
1803 | *val = FALSE; | |
1804 | return (0); | |
1805 | } | |
1806 | } | |
1807 | ||
1808 | static krb5_error_code | |
1809 | get_credv5_akimpersonate(krb5_context context, | |
1810 | char* keytab, | |
1811 | krb5_principal service_principal, | |
1812 | krb5_principal client_principal, | |
1813 | time_t starttime, | |
1814 | time_t endtime, | |
1815 | int *allowed_enctypes, | |
1816 | int *paddress, | |
1817 | krb5_creds** out_creds /* out */ ) | |
1818 | { | |
1819 | #if defined(USING_HEIMDAL) || (defined(HAVE_ENCODE_KRB5_ENC_TKT) && defined(HAVE_ENCODE_KRB5_TICKET) && defined(HAVE_KRB5_C_ENCRYPT)) | |
1820 | krb5_error_code code; | |
1821 | krb5_keytab kt = 0; | |
1822 | krb5_kt_cursor cursor[1]; | |
1823 | krb5_keytab_entry entry[1]; | |
1824 | krb5_ccache cc = 0; | |
1825 | krb5_creds *creds = 0; | |
1826 | krb5_enctype enctype; | |
1827 | krb5_kvno kvno; | |
1828 | krb5_keyblock session_key[1]; | |
1829 | #if USING_HEIMDAL | |
1830 | Ticket ticket_reply[1]; | |
1831 | EncTicketPart enc_tkt_reply[1]; | |
1832 | krb5_address address[30]; | |
1833 | krb5_addresses faddr[1]; | |
1834 | unsigned int temp_vno[1]; | |
1835 | time_t temp_time[2]; | |
1836 | #else | |
1837 | krb5_ticket ticket_reply[1]; | |
1838 | krb5_enc_tkt_part enc_tkt_reply[1]; | |
1839 | krb5_address address[30], *faddr[30]; | |
1840 | krb5_data * temp; | |
1841 | #endif | |
1842 | int i; | |
1843 | static int any_enctype[] = {0}; | |
1844 | *out_creds = 0; | |
1845 | if (!(creds = malloc(sizeof *creds))) { | |
1846 | code = ENOMEM; | |
1847 | goto cleanup; | |
1848 | } | |
1849 | if (!allowed_enctypes) | |
1850 | allowed_enctypes = any_enctype; | |
1851 | ||
1852 | cc = 0; | |
1853 | enctype = 0; /* AKIMPERSONATE_IGNORE_ENCTYPE */ | |
1854 | kvno = 0; /* AKIMPERSONATE_IGNORE_VNO */ | |
1855 | memset((char*)creds, 0, sizeof *creds); | |
1856 | memset((char*)entry, 0, sizeof *entry); | |
1857 | memset((char*)session_key, 0, sizeof *session_key); | |
1858 | memset((char*)ticket_reply, 0, sizeof *ticket_reply); | |
1859 | memset((char*)enc_tkt_reply, 0, sizeof *enc_tkt_reply); | |
1860 | code = krb5_kt_resolve(context, keytab, &kt); | |
1861 | if (code) { | |
1862 | if (keytab) | |
1863 | afs_com_err(progname, code, "while resolving keytab %s", keytab); | |
1864 | else | |
1865 | afs_com_err(progname, code, "while resolving default keytab"); | |
1866 | goto cleanup; | |
1867 | } | |
1868 | ||
1869 | if (service_principal) { | |
1870 | for (i = 0; (enctype = allowed_enctypes[i]) || !i; ++i) { | |
1871 | code = krb5_kt_get_entry(context, | |
1872 | kt, | |
1873 | service_principal, | |
1874 | kvno, | |
1875 | enctype, | |
1876 | entry); | |
1877 | if (!code) { | |
1878 | if (allowed_enctypes[i]) | |
1879 | deref_keyblock_enctype(session_key) = allowed_enctypes[i]; | |
1880 | break; | |
1881 | } | |
1882 | } | |
1883 | if (code) { | |
1884 | afs_com_err(progname, code,"while scanning keytab entries"); | |
1885 | goto cleanup; | |
1886 | } | |
1887 | } else { | |
1888 | krb5_keytab_entry new[1]; | |
1889 | int best = -1; | |
1890 | memset(new, 0, sizeof *new); | |
1891 | if ((code = krb5_kt_start_seq_get(context, kt, cursor))) { | |
1892 | afs_com_err(progname, code, "while starting keytab scan"); | |
1893 | goto cleanup; | |
1894 | } | |
1895 | while (!(code = krb5_kt_next_entry(context, kt, new, cursor))) { | |
1896 | for (i = 0; | |
1897 | allowed_enctypes[i] && allowed_enctypes[i] | |
1898 | != deref_entry_enctype(new); ++i) | |
1899 | ; | |
1900 | if ((!i || allowed_enctypes[i]) && | |
1901 | (best < 0 || best > i)) { | |
1902 | krb5_free_keytab_entry_contents(context, entry); | |
1903 | *entry = *new; | |
1904 | memset(new, 0, sizeof *new); | |
1905 | } else krb5_free_keytab_entry_contents(context, new); | |
1906 | } | |
1907 | if ((i = krb5_kt_end_seq_get(context, kt, cursor))) { | |
1908 | afs_com_err(progname, i, "while ending keytab scan"); | |
1909 | code = i; | |
1910 | goto cleanup; | |
1911 | } | |
1912 | if (best < 0) { | |
1913 | afs_com_err(progname, code, "while scanning keytab"); | |
1914 | goto cleanup; | |
1915 | } | |
1916 | deref_keyblock_enctype(session_key) = deref_entry_enctype(entry); | |
1917 | } | |
1918 | ||
1919 | /* Make Ticket */ | |
1920 | ||
1921 | #if USING_HEIMDAL | |
1922 | if ((code = krb5_generate_random_keyblock(context, | |
1923 | deref_keyblock_enctype(session_key), session_key))) { | |
1924 | afs_com_err(progname, code, "while making session key"); | |
1925 | goto cleanup; | |
1926 | } | |
1927 | enc_tkt_reply->flags.initial = 1; | |
1928 | enc_tkt_reply->transited.tr_type = DOMAIN_X500_COMPRESS; | |
1929 | enc_tkt_reply->cname = client_principal->name; | |
1930 | enc_tkt_reply->crealm = client_principal->realm; | |
1931 | enc_tkt_reply->key = *session_key; | |
1932 | { | |
1933 | static krb5_data empty_string; | |
1934 | enc_tkt_reply->transited.contents = empty_string; | |
1935 | } | |
1936 | enc_tkt_reply->authtime = starttime; | |
1937 | enc_tkt_reply->starttime = temp_time; | |
1938 | *enc_tkt_reply->starttime = starttime; | |
1939 | #if 0 | |
1940 | enc_tkt_reply->renew_till = temp_time + 1; | |
1941 | *enc_tkt_reply->renew_till = endtime; | |
1942 | #endif | |
1943 | enc_tkt_reply->endtime = endtime; | |
1944 | #else | |
1945 | if ((code = krb5_c_make_random_key(context, | |
1946 | deref_keyblock_enctype(session_key), session_key))) { | |
1947 | afs_com_err(progname, code, "while making session key"); | |
1948 | goto cleanup; | |
1949 | } | |
1950 | enc_tkt_reply->magic = KV5M_ENC_TKT_PART; | |
1951 | #define DATACAST (unsigned char *) | |
1952 | enc_tkt_reply->flags |= TKT_FLG_INITIAL; | |
1953 | enc_tkt_reply->transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; | |
1954 | enc_tkt_reply->session = session_key; | |
1955 | enc_tkt_reply->client = client_principal; | |
1956 | { | |
1957 | static krb5_data empty_string; | |
1958 | enc_tkt_reply->transited.tr_contents = empty_string; | |
1959 | } | |
1960 | enc_tkt_reply->times.authtime = starttime; | |
1961 | enc_tkt_reply->times.starttime = starttime; /* krb524init needs this */ | |
1962 | enc_tkt_reply->times.endtime = endtime; | |
1963 | #endif /* USING_HEIMDAL */ | |
1964 | /* NB: We will discard address for now--ignoring caddr field | |
1965 | in any case. MIT branch does what it always did. */ | |
1966 | ||
1967 | if (paddress && *paddress) { | |
1968 | deref_enc_tkt_addrs(enc_tkt_reply) = faddr; | |
1969 | #if USING_HEIMDAL | |
1970 | faddr->len = 0; | |
1971 | faddr->val = address; | |
1972 | #endif | |
1973 | for (i = 0; paddress[i]; ++i) { | |
1974 | #if USING_HEIMDAL | |
1975 | address[i].addr_type = KRB5_ADDRESS_INET; | |
1976 | address[i].address.data = (void*)(paddress+i); | |
1977 | address[i].address.length = sizeof(paddress[i]); | |
1978 | #else | |
1979 | #if !USING_SSL | |
1980 | address[i].magic = KV5M_ADDRESS; | |
1981 | address[i].addrtype = ADDRTYPE_INET; | |
1982 | #else | |
1983 | address[i].addrtype = AF_INET; | |
1984 | #endif | |
1985 | address[i].contents = (void*)(paddress+i); | |
1986 | address[i].length = sizeof(int); | |
1987 | faddr[i] = address+i; | |
1988 | #endif | |
1989 | } | |
1990 | #if USING_HEIMDAL | |
1991 | faddr->len = i; | |
1992 | #else | |
1993 | faddr[i] = 0; | |
1994 | #endif | |
1995 | } | |
1996 | ||
1997 | #if USING_HEIMDAL | |
1998 | ticket_reply->sname = service_principal->name; | |
1999 | ticket_reply->realm = service_principal->realm; | |
2000 | ||
2001 | { /* crypto block */ | |
2002 | krb5_crypto crypto = 0; | |
2003 | unsigned char *buf = 0; | |
2004 | size_t buf_size, buf_len; | |
2005 | char *what; | |
2006 | ||
2007 | ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, | |
2008 | enc_tkt_reply, &buf_len, code); | |
2009 | if(code) { | |
2010 | afs_com_err(progname, code, "while encoding ticket"); | |
2011 | goto cleanup; | |
2012 | } | |
2013 | ||
2014 | if(buf_len != buf_size) { | |
2015 | afs_com_err(progname, code, | |
2016 | "%u != %u while encoding ticket (internal ASN.1 encoder error", | |
2017 | (unsigned int)buf_len, (unsigned int)buf_size); | |
2018 | goto cleanup; | |
2019 | } | |
2020 | what = "krb5_crypto_init"; | |
2021 | code = krb5_crypto_init(context, | |
2022 | &deref_entry_keyblock(entry), | |
2023 | deref_entry_enctype(entry), | |
2024 | &crypto); | |
2025 | if(!code) { | |
2026 | what = "krb5_encrypt"; | |
2027 | code = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_TICKET, | |
2028 | buf, buf_len, entry->vno, &(ticket_reply->enc_part)); | |
2029 | } | |
2030 | if (buf) free(buf); | |
2031 | if (crypto) krb5_crypto_destroy(context, crypto); | |
2032 | if(code) { | |
2033 | afs_com_err(progname, code, "while %s", what); | |
2034 | goto cleanup; | |
2035 | } | |
2036 | } /* crypto block */ | |
2037 | ticket_reply->enc_part.etype = deref_entry_enctype(entry); | |
2038 | ticket_reply->enc_part.kvno = (void *)temp_vno; | |
2039 | *ticket_reply->enc_part.kvno = entry->vno; | |
2040 | ticket_reply->tkt_vno = 5; | |
2041 | #else | |
2042 | ticket_reply->server = service_principal; | |
2043 | ticket_reply->enc_part2 = enc_tkt_reply; | |
2044 | if ((code = krb5_encrypt_tkt_part(context, &deref_entry_keyblock(entry), ticket_reply))) { | |
2045 | afs_com_err(progname, code, "while making ticket"); | |
2046 | goto cleanup; | |
2047 | } | |
2048 | ticket_reply->enc_part.kvno = entry->vno; | |
2049 | #endif | |
2050 | ||
2051 | /* Construct Creds */ | |
2052 | ||
2053 | if ((code = krb5_copy_principal(context, service_principal, | |
2054 | &creds->server))) { | |
2055 | afs_com_err(progname, code, "while copying service principal"); | |
2056 | goto cleanup; | |
2057 | } | |
2058 | if ((code = krb5_copy_principal(context, client_principal, | |
2059 | &creds->client))) { | |
2060 | afs_com_err(progname, code, "while copying client principal"); | |
2061 | goto cleanup; | |
2062 | } | |
2063 | if ((code = krb5_copy_keyblock_contents(context, session_key, | |
2064 | &deref_session_key(creds)))) { | |
2065 | afs_com_err(progname, code, "while copying session key"); | |
2066 | goto cleanup; | |
2067 | } | |
2068 | ||
2069 | #if USING_HEIMDAL | |
2070 | creds->times.authtime = enc_tkt_reply->authtime; | |
2071 | creds->times.starttime = *(enc_tkt_reply->starttime); | |
2072 | creds->times.endtime = enc_tkt_reply->endtime; | |
2073 | creds->times.renew_till = 0; /* *(enc_tkt_reply->renew_till) */ | |
2074 | creds->flags.b = enc_tkt_reply->flags; | |
2075 | #else | |
2076 | creds->times = enc_tkt_reply->times; | |
2077 | creds->ticket_flags = enc_tkt_reply->flags; | |
2078 | #endif | |
2079 | if (!deref_enc_tkt_addrs(enc_tkt_reply)) | |
2080 | ; | |
2081 | else if ((code = krb5_copy_addresses(context, | |
2082 | deref_enc_tkt_addrs(enc_tkt_reply), &creds->addresses))) { | |
2083 | afs_com_err(progname, code, "while copying addresses"); | |
2084 | goto cleanup; | |
2085 | } | |
2086 | ||
2087 | #if USING_HEIMDAL | |
2088 | { | |
2089 | size_t creds_tkt_len; | |
2090 | ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length, | |
2091 | ticket_reply, &creds_tkt_len, code); | |
2092 | if(code) { | |
2093 | afs_com_err(progname, code, "while encoding ticket"); | |
2094 | goto cleanup; | |
2095 | } | |
2096 | } | |
2097 | #else | |
2098 | if ((code = encode_krb5_ticket(ticket_reply, &temp))) { | |
2099 | afs_com_err(progname, code, "while encoding ticket"); | |
2100 | goto cleanup; | |
2101 | } | |
2102 | creds->ticket = *temp; | |
2103 | free(temp); | |
2104 | #endif | |
2105 | /* return creds */ | |
2106 | *out_creds = creds; | |
2107 | creds = 0; | |
2108 | cleanup: | |
2109 | if (deref_enc_data(&ticket_reply->enc_part)) | |
2110 | free(deref_enc_data(&ticket_reply->enc_part)); | |
2111 | krb5_free_keytab_entry_contents(context, entry); | |
2112 | if (client_principal) | |
2113 | krb5_free_principal(context, client_principal); | |
2114 | if (service_principal) | |
2115 | krb5_free_principal(context, service_principal); | |
2116 | if (cc) | |
2117 | krb5_cc_close(context, cc); | |
2118 | if (kt) | |
2119 | krb5_kt_close(context, kt); | |
2120 | if (creds) krb5_free_creds(context, creds); | |
2121 | krb5_free_keyblock_contents(context, session_key); | |
2122 | return code; | |
2123 | #else | |
2124 | return -1; | |
2125 | #endif | |
2126 | } | |
2127 | ||
2128 | ||
2129 | static krb5_error_code | |
2130 | get_credv5(krb5_context context, char *name, char *inst, char *realm, | |
2131 | krb5_creds **creds) | |
2132 | { | |
2133 | krb5_creds increds; | |
2134 | krb5_error_code r; | |
2135 | static krb5_principal client_principal = 0; | |
2136 | ||
2137 | afs_dprintf("Getting tickets: %s%s%s@%s\n", name, | |
2138 | (inst && inst[0]) ? "/" : "", inst ? inst : "", realm); | |
2139 | ||
2140 | memset(&increds, 0, sizeof(increds)); | |
2141 | /* ANL - instance may be ptr to a null string. Pass null then */ | |
2142 | if ((r = krb5_build_principal(context, &increds.server, | |
2143 | strlen(realm), realm, | |
2144 | name, | |
2145 | (inst && strlen(inst)) ? inst : NULL, | |
2146 | NULL))) { | |
2147 | return r; | |
2148 | } | |
2149 | ||
2150 | ||
2151 | if (!_krb425_ccache) { | |
2152 | r = krb5_cc_default(context, &_krb425_ccache); | |
2153 | if (r) | |
2154 | return r; | |
2155 | } | |
2156 | if (!client_principal) { | |
2157 | if (client) { | |
2158 | r = krb5_parse_name(context, client, &client_principal); | |
2159 | } else { | |
2160 | r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal); | |
2161 | } | |
2162 | if (r) | |
2163 | return r; | |
2164 | } | |
2165 | ||
2166 | increds.client = client_principal; | |
2167 | increds.times.endtime = 0; | |
2168 | if (do524) | |
2169 | /* Ask for DES since that is what V4 understands */ | |
2170 | get_creds_enctype((&increds)) = ENCTYPE_DES_CBC_CRC; | |
2171 | ||
2172 | if (keytab) { | |
2173 | int allowed_enctypes[] = { | |
2174 | ENCTYPE_DES_CBC_CRC, 0 | |
2175 | }; | |
2176 | ||
2177 | r = get_credv5_akimpersonate(context, | |
2178 | keytab, | |
2179 | increds.server, | |
2180 | increds.client, | |
2181 | 300, ((~0U)>>1), | |
2182 | allowed_enctypes, | |
2183 | 0 /* paddress */, | |
2184 | creds /* out */); | |
2185 | } else { | |
2186 | r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds); | |
2187 | } | |
2188 | return r; | |
2189 | } | |
2190 | ||
2191 | ||
2192 | static int | |
2193 | get_user_realm(krb5_context context, char **realm) | |
2194 | { | |
2195 | static krb5_principal client_principal = 0; | |
2196 | krb5_error_code r = 0; | |
2197 | ||
2198 | *realm = NULL; | |
2199 | ||
2200 | if (!_krb425_ccache) { | |
2201 | r = krb5_cc_default(context, &_krb425_ccache); | |
2202 | if (r) | |
2203 | return r; | |
2204 | } | |
2205 | if (!client_principal) { | |
2206 | if (client) { | |
2207 | r = krb5_parse_name(context, client, &client_principal); | |
2208 | } else { | |
2209 | r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal); | |
2210 | } | |
2211 | if (r) | |
2212 | return r; | |
2213 | } | |
2214 | ||
2215 | *realm = extract_realm(context, client_principal); | |
2216 | if (*realm == NULL) | |
2217 | return ENOMEM; | |
2218 | ||
2219 | return(r); | |
2220 | } |