Add x509cafile variable.
[shishi.git] / lib / tkts.c
blobb064b92bf7c1e54527a106a6bb7c84cb24938161
1 /* tkts.c --- Ticket set handling.
2 * Copyright (C) 2002, 2003, 2004, 2006 Simon Josefsson
4 * This file is part of Shishi.
6 * Shishi is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Shishi is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Shishi; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "internal.h"
24 struct Shishi_tkts
26 Shishi *handle;
27 Shishi_tkt **tkts;
28 int ntkts;
31 #define TICKET_FILE "tickets"
33 /**
34 * shishi_tkts_default_file_guess:
35 * @handle: Shishi library handle create by shishi_init().
37 * Guesses the default ticket filename; it is $SHISHI_TICKETS,
38 * $SHISHI_HOME/tickets, or $HOME/.shishi/tickets.
40 * Return value: Returns default tkts filename as a string that
41 * has to be deallocated with free() by the caller.
42 **/
43 char *
44 shishi_tkts_default_file_guess (Shishi * handle)
46 char *envfile;
48 envfile = getenv ("SHISHI_TICKETS");
49 if (envfile)
50 return xstrdup (envfile);
52 return shishi_cfg_userdirectory_file (handle, TICKET_FILE);
55 /**
56 * shishi_tkts_default_file:
57 * @handle: Shishi library handle create by shishi_init().
59 * Get filename of default ticket set.
61 * Return value: Returns the default ticket set filename used in the
62 * library. The string is not a copy, so don't modify or deallocate
63 * it.
64 **/
65 const char *
66 shishi_tkts_default_file (Shishi * handle)
68 if (!handle->tktsdefaultfile)
70 char *p;
72 p = shishi_tkts_default_file_guess (handle);
73 shishi_tkts_default_file_set (handle, p);
74 free (p);
77 return handle->tktsdefaultfile;
80 /**
81 * shishi_tkts_default_file_set:
82 * @handle: Shishi library handle create by shishi_init().
83 * @tktsfile: string with new default tkts file name, or
84 * NULL to reset to default.
86 * Set the default ticket set filename used in the library. The
87 * string is copied into the library, so you can dispose of the
88 * variable immediately after calling this function.
89 **/
90 void
91 shishi_tkts_default_file_set (Shishi * handle, const char *tktsfile)
93 if (handle->tktsdefaultfile)
94 free (handle->tktsdefaultfile);
95 if (tktsfile)
96 handle->tktsdefaultfile = xstrdup (tktsfile);
97 else
98 handle->tktsdefaultfile = NULL;
102 * shishi_tkts_default:
103 * @handle: Shishi library handle create by shishi_init().
105 * Get the default ticket set for library handle.
107 * Return value: Return the handle global ticket set.
109 Shishi_tkts *
110 shishi_tkts_default (Shishi * handle)
112 if (handle->tkts == NULL &&
113 (shishi_tkts (handle, &handle->tkts) != SHISHI_OK))
114 handle->tkts = NULL;
116 return handle->tkts;
120 shishi_tkts_default_to_file (Shishi_tkts * tkts)
122 return shishi_tkts_to_file (tkts, shishi_tkts_default_file (tkts->handle));
126 * shishi_tkts:
127 * @handle: shishi handle as allocated by shishi_init().
128 * @tkts: output pointer to newly allocated tkts handle.
130 * Get a new ticket set handle.
132 * Return value: Returns %SHISHI_OK iff successful.
135 shishi_tkts (Shishi * handle, Shishi_tkts ** tkts)
137 *tkts = xcalloc (1, sizeof (**tkts));
139 (*tkts)->handle = handle;
141 return SHISHI_OK;
145 * shishi_tkts_done:
146 * @tkts: ticket set handle as allocated by shishi_tkts().
148 * Deallocates all resources associated with ticket set. The ticket
149 * set handle must not be used in calls to other shishi_tkts_*()
150 * functions after this.
152 void
153 shishi_tkts_done (Shishi_tkts ** tkts)
155 if (!tkts || !*tkts)
156 return;
158 if ((*tkts)->tkts)
159 free ((*tkts)->tkts);
160 free (*tkts);
162 *tkts = NULL;
164 return;
168 * shishi_tkts_size:
169 * @tkts: ticket set handle as allocated by shishi_tkts().
171 * Get size of ticket set.
173 * Return value: Returns number of tickets stored in ticket set.
176 shishi_tkts_size (Shishi_tkts * tkts)
178 return tkts->ntkts;
182 * shishi_tkts_nth:
183 * @tkts: ticket set handle as allocated by shishi_tkts().
184 * @ticketno: integer indicating requested ticket in ticket set.
186 * Get the n:th ticket in ticket set.
188 * Return value: Returns a ticket handle to the ticketno:th ticket in
189 * the ticket set, or NULL if ticket set is invalid or ticketno is
190 * out of bounds. The first ticket is ticketno 0, the second
191 * ticketno 1, and so on.
193 Shishi_tkt *
194 shishi_tkts_nth (Shishi_tkts * tkts, int ticketno)
196 if (tkts == NULL || ticketno >= tkts->ntkts)
197 return NULL;
199 return tkts->tkts[ticketno];
203 * shishi_tkts_remove:
204 * @tkts: ticket set handle as allocated by shishi_tkts().
205 * @ticketno: ticket number of ticket in the set to remove. The first
206 * ticket is ticket number 0.
208 * Remove a ticket, indexed by @ticketno, in ticket set.
210 * Return value: Returns SHISHI_OK if succesful or if ticketno larger
211 * than size of ticket set.
214 shishi_tkts_remove (Shishi_tkts * tkts, int ticketno)
216 if (!tkts)
217 return SHISHI_INVALID_TKTS;
219 if (ticketno >= tkts->ntkts)
220 return SHISHI_OK;
222 if (ticketno < tkts->ntkts)
223 memmove (&tkts->tkts[ticketno], &tkts->tkts[ticketno + 1],
224 sizeof (*tkts->tkts) * (tkts->ntkts - ticketno - 1));
226 --tkts->ntkts;
228 if (tkts->ntkts > 0)
230 tkts->tkts = xrealloc (tkts->tkts, sizeof (*tkts->tkts) * tkts->ntkts);
232 else
234 if (tkts->tkts)
235 free (tkts->tkts);
236 tkts->tkts = NULL;
239 return SHISHI_OK;
243 * shishi_tkts_add:
244 * @tkts: ticket set handle as allocated by shishi_tkts().
245 * @tkt: ticket to be added to ticket set.
247 * Add a ticket to the ticket set. Only the pointer is stored, so if
248 * you modify @tkt, the ticket in the ticket set will also be
249 * modified.
251 * Return value: Returns SHISHI_OK iff succesful.
254 shishi_tkts_add (Shishi_tkts * tkts, Shishi_tkt * tkt)
256 if (!tkt)
257 return SHISHI_INVALID_TICKET;
259 if (tkts->ntkts++ == 0)
260 tkts->tkts = xmalloc (sizeof (*tkts->tkts));
261 else
262 tkts->tkts = xrealloc (tkts->tkts, sizeof (*tkts->tkts) * tkts->ntkts);
264 tkts->tkts[tkts->ntkts - 1] = tkt;
266 return SHISHI_OK;
270 * shishi_tkts_new:
271 * @tkts: ticket set handle as allocated by shishi_tkts().
272 * @ticket: input ticket variable.
273 * @enckdcreppart: input ticket detail variable.
274 * @kdcrep: input KDC-REP variable.
276 * Allocate a new ticket and add it to the ticket set.
278 * Note that @ticket, @enckdcreppart and @kdcrep are stored by
279 * reference, so you must not de-allocate them before the ticket is
280 * removed from the ticket set and de-allocated.
282 * Return value: Returns SHISHI_OK iff succesful.
285 shishi_tkts_new (Shishi_tkts * tkts,
286 Shishi_asn1 ticket, Shishi_asn1 enckdcreppart,
287 Shishi_asn1 kdcrep)
289 Shishi_tkt *tkt;
290 int res;
292 /* XXX Who will de-allocate these? */
293 tkt = shishi_tkt2 (tkts->handle, ticket, enckdcreppart, kdcrep);
295 res = shishi_tkts_add (tkts, tkt);
296 if (res != SHISHI_OK)
298 free (tkt);
299 return res;
302 return SHISHI_OK;
306 * shishi_tkts_read:
307 * @tkts: ticket set handle as allocated by shishi_tkts().
308 * @fh: file descriptor to read from.
310 * Read tickets from file descriptor and add them to the ticket set.
312 * Return value: Returns SHISHI_OK iff succesful.
315 shishi_tkts_read (Shishi_tkts * tkts, FILE * fh)
317 int res;
319 res = SHISHI_OK;
320 while (!feof (fh))
322 Shishi_asn1 ticket;
323 Shishi_asn1 enckdcreppart;
324 Shishi_asn1 kdcrep;
326 res = shishi_kdcrep_parse (tkts->handle, fh, &kdcrep);
327 if (res != SHISHI_OK)
329 res = SHISHI_OK;
330 break;
333 res = shishi_enckdcreppart_parse (tkts->handle, fh, &enckdcreppart);
334 if (res != SHISHI_OK)
335 break;
337 res = shishi_ticket_parse (tkts->handle, fh, &ticket);
338 if (res != SHISHI_OK)
339 break;
341 /* XXX Who will de-allocate these? */
342 res = shishi_tkts_new (tkts, ticket, enckdcreppart, kdcrep);
343 if (res != SHISHI_OK)
344 break;
346 if (VERBOSEASN1 (tkts->handle))
348 printf ("Read ticket for principal `':\n");
349 shishi_kdcrep_print (tkts->handle, stdout, kdcrep);
350 shishi_enckdcreppart_print (tkts->handle, stdout, enckdcreppart);
351 shishi_ticket_print (tkts->handle, stdout, ticket);
355 return res;
359 * shishi_tkts_from_file:
360 * @tkts: ticket set handle as allocated by shishi_tkts().
361 * @filename: filename to read tickets from.
363 * Read tickets from file and add them to the ticket set.
365 * Return value: Returns SHISHI_OK iff succesful.
368 shishi_tkts_from_file (Shishi_tkts * tkts, const char *filename)
370 FILE *fh;
371 int res;
373 fh = fopen (filename, "r");
374 if (fh == NULL)
375 return SHISHI_FOPEN_ERROR;
377 res = shishi_tkts_read (tkts, fh);
378 if (res != SHISHI_OK)
380 fclose (fh);
381 return res;
384 res = fclose (fh);
385 if (res != 0)
386 return SHISHI_IO_ERROR;
388 return SHISHI_OK;
392 * shishi_tkts_write:
393 * @tkts: ticket set handle as allocated by shishi_tkts().
394 * @fh: file descriptor to write tickets to.
396 * Write tickets in set to file descriptor.
398 * Return value: Returns SHISHI_OK iff succesful.
401 shishi_tkts_write (Shishi_tkts * tkts, FILE * fh)
403 int res;
404 int i;
406 for (i = 0; i < tkts->ntkts; i++)
408 res = shishi_kdcrep_print
409 (tkts->handle, fh, shishi_tkt_kdcrep (tkts->tkts[i]));
410 if (res != SHISHI_OK)
412 shishi_error_printf (tkts->handle,
413 "Could not print ticket: %s",
414 shishi_error (tkts->handle));
415 return res;
418 res = shishi_enckdcreppart_print
419 (tkts->handle, fh, shishi_tkt_enckdcreppart (tkts->tkts[i]));
420 if (res != SHISHI_OK)
422 shishi_error_printf (tkts->handle,
423 "Could not print ticket: %s",
424 shishi_error (tkts->handle));
425 return res;
428 res = shishi_ticket_print (tkts->handle, fh,
429 shishi_tkt_ticket (tkts->tkts[i]));
430 if (res != SHISHI_OK)
432 shishi_error_printf (tkts->handle,
433 "Could not print ticket: %s",
434 shishi_error (tkts->handle));
435 return res;
438 fprintf (fh, "\n\n");
441 return SHISHI_OK;
445 * shishi_tkts_expire:
446 * @tkts: ticket set handle as allocated by shishi_tkts().
448 * Remove expired tickets from ticket set.
450 * Return value: Returns SHISHI_OK iff succesful.
453 shishi_tkts_expire (Shishi_tkts * tkts)
455 int warn = 0;
456 int i = 0;
458 while (i < tkts->ntkts)
460 if (shishi_tkt_expired_p (tkts->tkts[i]))
462 warn++;
463 shishi_tkts_remove (tkts, i);
465 else
466 i++;
469 if (VERBOSE (tkts->handle) && warn)
470 shishi_warn (tkts->handle,
471 ngettext ("removed %d expired ticket\n",
472 "removed %d expired tickets\n", warn), warn);
474 return SHISHI_OK;
478 * shishi_tkts_to_file:
479 * @tkts: ticket set handle as allocated by shishi_tkts().
480 * @filename: filename to write tickets to.
482 * Write tickets in set to file.
484 * Return value: Returns SHISHI_OK iff succesful.
487 shishi_tkts_to_file (Shishi_tkts * tkts, const char *filename)
489 FILE *fh;
490 int res;
492 fh = fopen (filename, "w");
493 if (fh == NULL)
494 return SHISHI_FOPEN_ERROR;
496 res = shishi_tkts_write (tkts, fh);
497 if (res != SHISHI_OK)
499 fclose (fh);
500 return res;
503 res = fclose (fh);
504 if (res != 0)
505 return SHISHI_IO_ERROR;
507 return SHISHI_OK;
511 * shishi_tkts_print_for_service:
512 * @tkts: ticket set handle as allocated by shishi_tkts().
513 * @fh: file descriptor to print to.
514 * @service: service to limit tickets printed to, or NULL.
516 * Print description of tickets for specified service to file
517 * descriptor. If service is NULL, all tickets are printed.
519 * Return value: Returns SHISHI_OK iff succesful.
522 shishi_tkts_print_for_service (Shishi_tkts * tkts, FILE * fh,
523 const char *service)
525 int res;
526 int found;
527 int i;
529 found = 0;
530 for (i = 0; i < shishi_tkts_size (tkts); i++)
532 Shishi_tkt *tkt = shishi_tkts_nth (tkts, i);
534 if (service)
536 char *buf;
538 res = shishi_tkt_server (tkt, &buf, NULL);
539 if (res != SHISHI_OK)
540 continue;
542 if (strcmp (service, buf) != 0)
544 free (buf);
545 continue;
548 free (buf);
551 printf ("\n");
552 shishi_tkt_pretty_print (shishi_tkts_nth (tkts, i), fh);
553 found++;
556 if (found)
558 printf (ngettext ("\n%d ticket found.\n", "\n%d tickets found.\n",
559 found), found);
561 else
563 if (service)
564 printf ("\nNo matching tickets found.\n");
565 else
566 printf ("\nNo tickets found.\n");
569 return SHISHI_OK;
573 * shishi_tkts_print:
574 * @tkts: ticket set handle as allocated by shishi_tkts().
575 * @fh: file descriptor to print to.
577 * Print description of all tickets to file descriptor.
579 * Return value: Returns SHISHI_OK iff succesful.
582 shishi_tkts_print (Shishi_tkts * tkts, FILE * fh)
584 return shishi_tkts_print_for_service (tkts, fh, NULL);
588 * shishi_tkt_match_p:
589 * @tkt: ticket to test hints on.
590 * @hint: structure with characteristics of ticket to be found.
592 * Test if a ticket matches specified hints.
594 * Return value: Returns 0 iff ticket fails to match given criteria.
597 shishi_tkt_match_p (Shishi_tkt * tkt, Shishi_tkts_hint * hint)
599 if (hint->server && !shishi_tkt_server_p (tkt, hint->server))
600 return 0;
602 if (hint->client && !shishi_tkt_client_p (tkt, hint->client))
603 return 0;
605 if (!(hint->flags & SHISHI_TKTSHINTFLAGS_ACCEPT_EXPIRED) &&
606 !shishi_tkt_valid_now_p (tkt))
607 return 0;
609 if ((hint->tktflags & SHISHI_TICKETFLAGS_FORWARDABLE) &&
610 !shishi_tkt_forwardable_p (tkt))
611 return 0;
613 if ((hint->tktflags & SHISHI_TICKETFLAGS_FORWARDED) &&
614 !shishi_tkt_forwarded_p (tkt))
615 return 0;
617 if ((hint->tktflags & SHISHI_TICKETFLAGS_RENEWABLE) &&
618 !shishi_tkt_renewable_p (tkt))
619 return 0;
621 if ((hint->tktflags & SHISHI_TICKETFLAGS_PROXIABLE) &&
622 !shishi_tkt_proxiable_p (tkt))
623 return 0;
625 if ((hint->tktflags & SHISHI_TICKETFLAGS_PROXY) &&
626 !shishi_tkt_proxy_p (tkt))
627 return 0;
629 if (hint->etype && !shishi_tkt_keytype_p (tkt, hint->etype))
630 return 0;
632 return 1;
636 * shishi_tkts_find:
637 * @tkts: ticket set handle as allocated by shishi_tkts().
638 * @hint: structure with characteristics of ticket to be found.
640 * Search the ticketset sequentially (from ticket number 0 through all
641 * tickets in the set) for a ticket that fits the given
642 * characteristics. If a ticket is found, the hint->startpos field is
643 * updated to point to the next ticket in the set, so this function
644 * can be called repeatedly with the same hint argument in order to
645 * find all tickets matching a certain criterium. Note that if
646 * tickets are added to, or removed from, the ticketset during a query
647 * with the same hint argument, the hint->startpos field must be
648 * updated appropriately.
650 * Here is how you would typically use this function:
652 * Shishi_tkts_hint hint;
654 * Shishi_tkt tkt;
656 * ...
658 * memset(&hint, 0, sizeof(hint));
660 * hint.server = "imap/mail.example.org";
662 * tkt = shishi_tkts_find (shishi_tkts_default(handle), &hint);
664 * if (!tkt)
666 * printf("No ticket found...\n");
668 * else
670 * ...do something with ticket
672 * Return value: Returns a ticket if found, or NULL if no further
673 * matching tickets could be found.
675 Shishi_tkt *
676 shishi_tkts_find (Shishi_tkts * tkts, Shishi_tkts_hint * hint)
678 int i;
680 if (VERBOSENOISE (tkts->handle))
682 fprintf (stderr, "Searching tickets... ");
683 if (hint->server)
684 fprintf (stderr, "server=`%s' ", hint->server);
685 if (hint->client)
686 fprintf (stderr, "client=`%s' ", hint->client);
687 fprintf (stderr, "\n");
690 for (i = hint->startpos; i < tkts->ntkts; i++)
692 if (!shishi_tkt_match_p (tkts->tkts[i], hint))
693 continue;
695 hint->startpos = i + 1;
696 return tkts->tkts[i];
699 hint->startpos = i;
700 return NULL;
704 * shishi_tkts_find_for_clientserver:
705 * @tkts: ticket set handle as allocated by shishi_tkts().
706 * @client: client name to find ticket for.
707 * @server: server name to find ticket for.
709 * Short-hand function for searching the ticket set for a ticket for
710 * the given client and server. See shishi_tkts_find().
712 * Return value: Returns a ticket if found, or NULL.
714 Shishi_tkt *
715 shishi_tkts_find_for_clientserver (Shishi_tkts * tkts,
716 const char *client, const char *server)
718 Shishi_tkts_hint hint;
719 Shishi_tkt *tkt;
721 memset (&hint, 0, sizeof (hint));
722 hint.server = (char *) server;
723 hint.client = (char *) client;
725 tkt = shishi_tkts_find (tkts, &hint);
727 return tkt;
731 * shishi_tkts_find_for_server:
732 * @tkts: ticket set handle as allocated by shishi_tkts().
733 * @server: server name to find ticket for.
735 * Short-hand function for searching the ticket set for a ticket for
736 * the given server using the default client principal. See
737 * shishi_tkts_find_for_clientserver() and shishi_tkts_find().
739 * Return value: Returns a ticket if found, or NULL.
741 Shishi_tkt *
742 shishi_tkts_find_for_server (Shishi_tkts * tkts, const char *server)
744 return shishi_tkts_find_for_clientserver
745 (tkts, shishi_principal_default (tkts->handle), server);
748 /* Set flags and times in KDC-REQ based on hint. */
749 static int
750 act_hint_on_kdcreq (Shishi * handle,
751 Shishi_tkts_hint * hint, Shishi_asn1 kdcreq)
753 time_t starttime = hint->starttime ? hint->starttime : time (NULL);
754 time_t endtime = hint->endtime ? hint->endtime :
755 starttime + handle->ticketlife;
756 time_t renew_till = hint->renew_till ? hint->renew_till :
757 starttime + handle->renewlife;
758 int rc;
760 if (hint->starttime)
762 rc = shishi_asn1_write (handle, kdcreq, "req-body.from",
763 shishi_generalize_time (handle, starttime), 0);
764 if (rc != SHISHI_OK)
766 shishi_error_printf (handle, "Cannot set starttime: %s",
767 shishi_strerror (rc));
768 return rc;
772 if (hint->endtime)
774 rc = shishi_asn1_write (handle, kdcreq, "req-body.till",
775 shishi_generalize_time (handle, endtime), 0);
776 if (rc != SHISHI_OK)
778 shishi_error_printf (handle, "Cannot set endtime: %s",
779 shishi_strerror (rc));
780 return rc;
784 if (hint->tktflags & SHISHI_TICKETFLAGS_FORWARDABLE)
786 rc = shishi_kdcreq_options_add (handle, kdcreq,
787 SHISHI_KDCOPTIONS_FORWARDABLE);
788 if (rc != SHISHI_OK)
789 goto done;
792 if (hint->tktflags & SHISHI_TICKETFLAGS_FORWARDED)
794 rc = shishi_kdcreq_options_add (handle, kdcreq,
795 SHISHI_KDCOPTIONS_FORWARDED);
796 if (rc != SHISHI_OK)
797 goto done;
800 if (hint->tktflags & SHISHI_TICKETFLAGS_RENEWABLE)
802 rc = shishi_kdcreq_options_add (handle, kdcreq,
803 SHISHI_KDCOPTIONS_RENEWABLE);
804 if (rc != SHISHI_OK)
805 goto done;
807 rc = shishi_asn1_write (handle, kdcreq, "req-body.rtime",
808 shishi_generalize_time (handle, renew_till), 0);
809 if (rc != SHISHI_OK)
811 shishi_error_printf (handle, "Cannot set renewtill: %s",
812 shishi_strerror (rc));
813 return rc;
817 if (hint->tktflags & SHISHI_TICKETFLAGS_PROXIABLE)
819 rc = shishi_kdcreq_options_add (handle, kdcreq,
820 SHISHI_KDCOPTIONS_PROXIABLE);
821 if (rc != SHISHI_OK)
822 goto done;
825 if (hint->tktflags & SHISHI_TICKETFLAGS_PROXY)
827 rc = shishi_kdcreq_options_add (handle, kdcreq,
828 SHISHI_KDCOPTIONS_PROXY);
829 if (rc != SHISHI_OK)
830 goto done;
834 if (hint->etype)
836 rc = shishi_kdcreq_set_etype (handle, kdcreq, &hint->etype, 1);
837 if (rc != SHISHI_OK)
838 goto done;
841 return SHISHI_OK;
843 done:
844 shishi_error_printf (handle, "Cannot set KDC Options: %s",
845 shishi_strerror (rc));
846 return rc;
849 /* Make sure the ticket granting ticket is suitable for the wanted
850 ticket. E.g., if the wanted ticket should be a PROXY ticket, the
851 ticket granting ticket must be a PROXIABLE ticket for things to
852 work. */
853 static void
854 set_tgtflags_based_on_hint (Shishi_tkts_hint * tkthint,
855 Shishi_tkts_hint * tgthint)
857 if (tkthint->tktflags & SHISHI_TICKETFLAGS_FORWARDABLE)
858 tgthint->tktflags |= SHISHI_TICKETFLAGS_FORWARDABLE;
860 if (tkthint->tktflags & SHISHI_TICKETFLAGS_FORWARDED)
861 tgthint->tktflags |= SHISHI_TICKETFLAGS_FORWARDABLE;
863 if (tkthint->tktflags & SHISHI_TICKETFLAGS_PROXIABLE)
864 tgthint->tktflags |= SHISHI_TICKETFLAGS_PROXIABLE;
866 if (tkthint->tktflags & SHISHI_TICKETFLAGS_PROXY)
867 tgthint->tktflags |= SHISHI_TICKETFLAGS_PROXIABLE;
869 if (tkthint->tktflags & SHISHI_TICKETFLAGS_RENEWABLE)
870 tgthint->tktflags |= SHISHI_TICKETFLAGS_RENEWABLE;
872 if (tkthint->kdcoptions & SHISHI_KDCOPTIONS_RENEW)
873 tgthint->tktflags |= SHISHI_TICKETFLAGS_RENEWABLE;
875 if (tkthint->endtime)
876 tgthint->endtime = tkthint->endtime;
878 if (tkthint->passwd)
879 tgthint->passwd = tkthint->passwd;
881 if (tkthint->preauthetype)
882 tgthint->preauthetype = tkthint->preauthetype;
884 if (tkthint->preauthsalt)
886 tgthint->preauthsalt = tkthint->preauthsalt;
887 tgthint->preauthsaltlen = tkthint->preauthsaltlen;
890 if (tkthint->preauths2kparams)
892 tgthint->preauths2kparams = tkthint->preauths2kparams;
893 tgthint->preauths2kparamslen = tkthint->preauths2kparamslen;
897 /* Pre-authenticate request, based on LOCHINT. Currently only
898 PA-ENC-TIMESTAMP is supported. */
899 static int
900 do_preauth (Shishi_tkts *tkts, Shishi_tkts_hint *lochint, Shishi_as *as)
902 int rc = SHISHI_OK;
904 if (lochint->preauthetype)
906 Shishi_key *key;
907 char *user;
909 /* XXX Don't prompt for password here? */
911 rc = shishi_asreq_clientrealm (tkts->handle, shishi_as_req (as),
912 &user, NULL);
913 if (rc != SHISHI_OK)
914 return rc;
916 rc = shishi_prompt_password (tkts->handle, &lochint->passwd,
917 "Enter password for `%s': ", user);
918 if (rc != SHISHI_OK)
919 return rc;
921 if (!lochint->preauthsalt)
923 rc = shishi_derive_default_salt (tkts->handle, user,
924 &lochint->preauthsalt);
925 if (rc != SHISHI_OK)
926 return rc;
928 lochint->preauthsaltlen = strlen (lochint->preauthsalt);
931 rc = shishi_key_from_string (tkts->handle, lochint->preauthetype,
932 lochint->passwd, strlen (lochint->passwd),
933 lochint->preauthsalt,
934 lochint->preauthsaltlen,
935 lochint->preauths2kparams, &key);
936 if (rc != SHISHI_OK)
937 return rc;
939 rc = shishi_kdcreq_add_padata_preauth (tkts->handle,
940 shishi_as_req (as), key);
943 return rc;
946 /* Handle ETYPE-INFO and ETYPE-INFO2 pre-auth data. */
947 static int
948 recover_preauth_info (Shishi_tkts *tkts,
949 Shishi_as *as,
950 Shishi_tkts_hint *lochint,
951 Shishi_asn1 einfos,
952 bool isinfo2,
953 bool *retry)
955 size_t foundpos = SIZE_MAX;
956 size_t i, n;
957 int rc;
959 shishi_verbose (tkts->handle, "Found INFO-ETYPE(2) pre-auth hints");
961 if (VERBOSEASN1(tkts->handle))
963 if (isinfo2)
964 shishi_etype_info2_print (tkts->handle, stdout, einfos);
965 else
966 shishi_etype_info_print (tkts->handle, stdout, einfos);
969 if (lochint->preauthetype)
971 shishi_verbose (tkts->handle, "Pre-auth data already specified");
972 return SHISHI_OK;
975 rc = shishi_asn1_number_of_elements (tkts->handle, einfos, "", &n);
976 if (rc != SHISHI_OK)
977 return rc;
979 for (i = 1; i <= n; i++)
981 char *format;
982 int32_t etype;
984 format = xasprintf ("?%d.etype", i);
985 rc = shishi_asn1_read_int32 (tkts->handle, einfos, format, &etype);
986 free (format);
987 if (rc == SHISHI_OK)
989 size_t j;
991 shishi_verbose (tkts->handle, "Server has etype %d", etype);
993 for (j = 0; j < tkts->handle->nclientkdcetypes; j++)
995 if (etype == tkts->handle->clientkdcetypes[j])
997 if (j < foundpos && VERBOSENOISE(tkts->handle))
999 char *salt;
1000 size_t saltlen;
1002 shishi_verbose (tkts->handle, "New best etype %d", etype);
1004 /* XXX mem leak. */
1006 format = xasprintf ("?%d.salt", i);
1007 rc = shishi_asn1_read (tkts->handle, einfos, format,
1008 &lochint->preauthsalt,
1009 &lochint->preauthsaltlen);
1010 free (format);
1011 if (rc != SHISHI_OK && rc != SHISHI_ASN1_NO_ELEMENT)
1012 return rc;
1014 if (isinfo2)
1016 format = xasprintf ("?%d.s2kparams", i);
1017 rc = shishi_asn1_read (tkts->handle, einfos, format,
1018 &lochint->preauths2kparams,
1019 &lochint->preauths2kparamslen);
1020 free (format);
1021 if (rc != SHISHI_OK && rc != SHISHI_ASN1_NO_ELEMENT)
1022 return rc;
1026 foundpos = MIN(foundpos, j);
1032 if (foundpos != SIZE_MAX)
1034 lochint->preauthetype = tkts->handle->clientkdcetypes[foundpos];
1036 shishi_verbose (tkts->handle, "Best pre-auth etype was %d",
1037 lochint->preauthetype);
1039 *retry = true;
1042 return SHISHI_OK;
1045 /* Called when KDC refused with a NEED_PREAUTH error. This function
1046 should look at the METHOD-DATA, figure out what kind of pre-auth is
1047 requested, and if it is able to figure out how to recover from the
1048 error, set *RETRY to true and set any hints in LOCHINT that help
1049 do_preauth() compute the proper pre-auth data. */
1050 static int
1051 recover_preauth (Shishi_tkts *tkts,
1052 Shishi_as *as,
1053 Shishi_tkts_hint *lochint,
1054 bool *retry)
1056 Shishi_asn1 krberror = shishi_as_krberror (as);
1057 Shishi_asn1 pas;
1058 size_t i, n;
1059 int rc;
1061 *retry = false;
1063 shishi_verbose (tkts->handle, "Server requests pre-auth data");
1065 rc = shishi_krberror_methoddata (tkts->handle, krberror, &pas);
1066 if (rc != SHISHI_OK)
1067 return rc;
1069 rc = shishi_asn1_number_of_elements (tkts->handle, pas, "", &n);
1070 if (rc == SHISHI_OK)
1072 for (i = 1; i <= n; i++)
1074 char *format = xasprintf ("?%d.padata-type", i);
1075 int32_t padatatype;
1077 rc = shishi_asn1_read_int32 (tkts->handle, pas, format,
1078 &padatatype);
1079 free (format);
1080 if (rc == SHISHI_OK)
1082 shishi_verbose (tkts->handle, "Looking at pa-type %d",
1083 padatatype);
1085 switch (padatatype)
1087 /* XXX Don't parse INFO structures if there is a
1088 INFO2. */
1090 case SHISHI_PA_ETYPE_INFO:
1091 case SHISHI_PA_ETYPE_INFO2:
1093 char *der;
1094 size_t len;
1095 Shishi_asn1 einfos;
1097 format = xasprintf ("?%d.padata-value", i);
1098 rc = shishi_asn1_read (tkts->handle, pas, format,
1099 &der, &len);
1100 free (format);
1101 if (rc != SHISHI_OK)
1103 shishi_error_printf (tkts->handle,
1104 "Can't extract PA-DATA value");
1105 continue;
1108 if (padatatype == SHISHI_PA_ETYPE_INFO)
1109 einfos = shishi_der2asn1_etype_info (tkts->handle,
1110 der, len);
1111 else
1112 einfos = shishi_der2asn1_etype_info2 (tkts->handle,
1113 der, len);
1114 free (der);
1115 if (!einfos)
1117 shishi_error_printf (tkts->handle,
1118 "Can't DER decode PA-DATA");
1119 continue;
1122 rc = recover_preauth_info
1123 (tkts, as, lochint, einfos,
1124 padatatype == SHISHI_PA_ETYPE_INFO2, retry);
1125 if (rc != SHISHI_OK)
1127 shishi_error_printf (tkts->handle,
1128 "Could not use pre-auth data: %s",
1129 shishi_strerror (rc));
1130 continue;
1133 shishi_asn1_done (tkts->handle, einfos);
1135 break;
1137 default:
1138 break;
1144 shishi_asn1_done (tkts->handle, pas);
1146 return SHISHI_OK;
1150 * shishi_tkts_get_tgt:
1151 * @tkts: ticket set handle as allocated by shishi_tkts().
1152 * @hint: structure with characteristics of ticket to begot.
1154 * Get a ticket granting ticket (TGT) suitable for acquiring ticket
1155 * matching the hint. I.e., get a TGT for the server realm in the
1156 * hint structure (hint->serverrealm), or the default realm if the
1157 * serverrealm field is NULL. Can result in AS exchange.
1159 * Currently this function do not implement cross realm logic.
1161 * This function is used by shishi_tkts_get(), which is probably what
1162 * you really want to use unless you have special needs.
1164 * Return value: Returns a ticket granting ticket if successful, or
1165 * NULL if this function is unable to acquire on.
1167 Shishi_tkt *
1168 shishi_tkts_get_tgt (Shishi_tkts * tkts, Shishi_tkts_hint * hint)
1170 Shishi_tkts_hint lochint;
1171 Shishi_as *as;
1172 Shishi_tkt *tgt;
1173 int rc;
1175 /* XXX cross-realm operation */
1177 memset (&lochint, 0, sizeof (lochint));
1178 asprintf (&lochint.server, "krbtgt/%s", hint->serverrealm ?
1179 hint->serverrealm : shishi_realm_default (tkts->handle));
1180 set_tgtflags_based_on_hint (hint, &lochint);
1182 tgt = shishi_tkts_find (tkts, &lochint);
1184 free (lochint.server);
1185 lochint.server = NULL;
1187 if (tgt)
1188 return tgt;
1190 again:
1191 rc = shishi_as (tkts->handle, &as);
1192 if (rc == SHISHI_OK)
1193 rc = act_hint_on_kdcreq (tkts->handle, &lochint, shishi_as_req (as));
1194 if (rc == SHISHI_OK)
1195 rc = do_preauth (tkts, &lochint, as);
1196 if (rc == SHISHI_OK)
1197 rc = shishi_as_req_build (as);
1198 if (rc == SHISHI_OK)
1199 rc = shishi_as_sendrecv_hint (as, &lochint);
1200 if (rc == SHISHI_OK)
1201 rc = shishi_as_rep_process (as, NULL, lochint.passwd);
1202 if (rc == SHISHI_GOT_KRBERROR
1203 && shishi_krberror_errorcode_fast (tkts->handle,
1204 shishi_as_krberror (as))
1205 == SHISHI_KDC_ERR_PREAUTH_REQUIRED)
1207 bool retry = false;
1209 rc = recover_preauth (tkts, as, &lochint, &retry);
1210 if (rc != SHISHI_OK)
1211 return NULL;
1213 if (retry)
1215 shishi_as_done (as);
1216 goto again;
1219 shishi_error_printf (tkts->handle, "Unsupported pre-auth required");
1220 return NULL;
1222 if (rc != SHISHI_OK)
1224 shishi_error_printf (tkts->handle,
1225 "AS exchange failed: %s\n%s\n",
1226 shishi_strerror (rc), shishi_error (tkts->handle));
1227 return NULL;
1230 /* XXX free lochint members */
1232 tgt = shishi_as_tkt (as);
1233 if (!tgt)
1235 shishi_error_printf (tkts->handle, "No ticket in AS-REP");
1236 return NULL;
1239 if (VERBOSENOISE (tkts->handle))
1241 printf ("Received ticket granting ticket:\n");
1242 shishi_tkt_pretty_print (tgt, stdout);
1245 rc = shishi_tkts_add (tkts, tgt);
1246 if (rc != SHISHI_OK)
1247 printf ("Could not add ticket: %s", shishi_strerror (rc));
1249 return tgt;
1253 * shishi_tkts_get_tgs:
1254 * @tkts: ticket set handle as allocated by shishi_tkts().
1255 * @hint: structure with characteristics of ticket to begot.
1256 * @tgt: ticket granting ticket to use.
1258 * Get a ticket via TGS exchange using specified ticket granting
1259 * ticket.
1261 * This function is used by shishi_tkts_get(), which is probably what
1262 * you really want to use unless you have special needs.
1264 * Return value: Returns a ticket if successful, or NULL if this
1265 * function is unable to acquire on.
1267 Shishi_tkt *
1268 shishi_tkts_get_tgs (Shishi_tkts * tkts,
1269 Shishi_tkts_hint * hint, Shishi_tkt * tgt)
1271 Shishi_tgs *tgs;
1272 Shishi_tkt *tkt;
1273 int rc;
1275 rc = shishi_tgs (tkts->handle, &tgs);
1276 shishi_tgs_tgtkt_set (tgs, tgt);
1277 if (rc == SHISHI_OK)
1278 rc = act_hint_on_kdcreq (tkts->handle, hint, shishi_tgs_req (tgs));
1279 if (rc == SHISHI_OK)
1280 rc = shishi_tgs_set_server (tgs, hint->server);
1281 if (rc == SHISHI_OK)
1282 rc = shishi_tgs_req_build (tgs);
1283 if (rc == SHISHI_OK)
1284 rc = shishi_tgs_sendrecv_hint (tgs, hint);
1285 if (rc == SHISHI_OK)
1286 rc = shishi_tgs_rep_process (tgs);
1287 if (rc != SHISHI_OK)
1289 shishi_error_printf (tkts->handle,
1290 "TGS exchange failed: %s\n%s\n",
1291 shishi_strerror (rc), shishi_error (tkts->handle));
1292 if (rc == SHISHI_GOT_KRBERROR)
1293 shishi_krberror_pretty_print (tkts->handle, stdout,
1294 shishi_tgs_krberror (tgs));
1295 return NULL;
1298 tkt = shishi_tgs_tkt (tgs);
1299 if (!tkt)
1301 shishi_error_printf (tkts->handle, "No ticket in TGS-REP?!: %s",
1302 shishi_error (tkts->handle));
1303 return NULL;
1306 if (VERBOSENOISE (tkts->handle))
1308 printf ("Received ticket:\n");
1309 shishi_tkt_pretty_print (tkt, stdout);
1312 rc = shishi_tkts_add (tkts, tkt);
1313 if (rc != SHISHI_OK)
1314 printf ("Could not add ticket: %s", shishi_strerror (rc));
1316 return tkt;
1320 * shishi_tkts_get:
1321 * @tkts: ticket set handle as allocated by shishi_tkts().
1322 * @hint: structure with characteristics of ticket to begot.
1324 * Get a ticket matching given characteristics. This function first
1325 * looks in the ticket set for the ticket, then tries to find a
1326 * suitable TGT, possibly via an AS exchange, using
1327 * shishi_tkts_get_tgt(), and then use that TGT in a TGS exchange to
1328 * get the ticket.
1330 * Currently this function do not implement cross realm logic.
1332 * Return value: Returns a ticket if found, or NULL if this function
1333 * is unable to get the ticket.
1335 Shishi_tkt *
1336 shishi_tkts_get (Shishi_tkts * tkts, Shishi_tkts_hint * hint)
1338 Shishi_tkt *tkt, *tgt;
1340 /* If we already have a matching ticket, avoid getting a new one. */
1341 hint->startpos = 0;
1342 tkt = shishi_tkts_find (tkts, hint);
1343 if (tkt)
1344 return tkt;
1346 tgt = shishi_tkts_get_tgt (tkts, hint);
1347 if (!tgt)
1349 shishi_error_printf (tkts->handle, "Could not get TGT for ticket.");
1350 return NULL;
1353 if (shishi_tkt_match_p (tgt, hint))
1354 return tgt;
1356 tkt = shishi_tkts_get_tgs (tkts, hint, tgt);
1357 if (!tkt)
1359 shishi_error_printf (tkts->handle, "Could not get ticket using TGT.");
1360 return NULL;
1363 return tkt;
1367 * shishi_tkts_get_for_clientserver:
1368 * @tkts: ticket set handle as allocated by shishi_tkts().
1369 * @client: client name to get ticket for.
1370 * @server: server name to get ticket for.
1372 * Short-hand function for getting a ticket for the given client and
1373 * server. See shishi_tkts_get().
1375 * Return value: Returns a ticket if found, or NULL.
1377 Shishi_tkt *
1378 shishi_tkts_get_for_clientserver (Shishi_tkts * tkts,
1379 const char *client, const char *server)
1381 Shishi_tkts_hint hint;
1382 Shishi_tkt *tkt;
1384 memset (&hint, 0, sizeof (hint));
1385 hint.client = (char *) client;
1386 hint.server = (char *) server;
1388 tkt = shishi_tkts_get (tkts, &hint);
1390 return tkt;
1394 * shishi_tkts_get_for_server:
1395 * @tkts: ticket set handle as allocated by shishi_tkts().
1396 * @server: server name to get ticket for.
1398 * Short-hand function for getting a ticket for the given server and
1399 * the default principal client. See shishi_tkts_get().
1401 * Return value: Returns a ticket if found, or NULL.
1403 Shishi_tkt *
1404 shishi_tkts_get_for_server (Shishi_tkts * tkts, const char *server)
1406 return shishi_tkts_get_for_clientserver
1407 (tkts, shishi_principal_default (tkts->handle), server);
1410 Shishi_tkt *
1411 shishi_tkts_get_for_localservicepasswd (Shishi_tkts * tkts,
1412 const char *service,
1413 const char *passwd)
1415 Shishi_tkt *tkt;
1416 Shishi_tkts_hint hint;
1418 memset (&hint, 0, sizeof (hint));
1419 hint.client = (char *) shishi_principal_default (tkts->handle);
1420 hint.server = shishi_server_for_local_service (tkts->handle, service);
1421 hint.passwd = (char *) passwd;
1423 tkt = shishi_tkts_get (tkts, &hint);
1425 free (hint.server);
1427 return tkt;