Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / lib / krb / tf_util.c
1 /* 
2   Copyright (C) 1989 by the Massachusetts Institute of Technology
3
4    Export of this software from the United States of America is assumed
5    to require a specific license from the United States Government.
6    It is the responsibility of any person or organization contemplating
7    export to obtain such a license before exporting.
8
9 WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10 distribute this software and its documentation for any purpose and
11 without fee is hereby granted, provided that the above copyright
12 notice appear in all copies and that both that copyright notice and
13 this permission notice appear in supporting documentation, and that
14 the name of M.I.T. not be used in advertising or publicity pertaining
15 to distribution of the software without specific, written prior
16 permission.  M.I.T. makes no representations about the suitability of
17 this software for any purpose.  It is provided "as is" without express
18 or implied warranty.
19
20   */
21         
22 #include "krb_locl.h"
23
24 RCSID("$Id: tf_util.c,v 1.39.2.2 2000/06/23 04:03:58 assar Exp $");
25
26
27 #define TOO_BIG -1
28 #define TF_LCK_RETRY ((unsigned)2)      /* seconds to sleep before
29                                          * retry if ticket file is
30                                          * locked */
31 #define TF_LCK_RETRY_COUNT      (50)    /* number of retries    */
32
33 #ifndef O_BINARY
34 #define O_BINARY 0
35 #endif
36
37 #define MAGIC_TICKET_NAME "magic"
38 #define MAGIC_TICKET_TIME_DIFF_INST "time-diff"
39 #define MAGIC_TICKET_ADDR_INST "our-address"
40
41 /*
42  * fd must be initialized to something that won't ever occur as a real
43  * file descriptor. Since open(2) returns only non-negative numbers as
44  * valid file descriptors, and tf_init always stuffs the return value
45  * from open in here even if it is an error flag, we must
46  *      a. Initialize fd to a negative number, to indicate that it is
47  *         not initially valid.
48  *      b. When checking for a valid fd, assume that negative values
49  *         are invalid (ie. when deciding whether tf_init has been
50  *         called.)
51  *      c. In tf_close, be sure it gets reinitialized to a negative
52  *         number. 
53  */
54 static  int fd = -1;
55 static  int curpos;                             /* Position in tfbfr */
56 static  int lastpos;                    /* End of tfbfr */
57 static  char tfbfr[BUFSIZ];             /* Buffer for ticket data */
58
59 static int tf_gets(char *s, int n);
60 static int tf_read(void *s, int n);
61
62 /*
63  * This file contains routines for manipulating the ticket cache file.
64  *
65  * The ticket file is in the following format:
66  *
67  *      principal's name        (null-terminated string)
68  *      principal's instance    (null-terminated string)
69  *      CREDENTIAL_1
70  *      CREDENTIAL_2
71  *      ...
72  *      CREDENTIAL_n
73  *      EOF
74  *
75  *      Where "CREDENTIAL_x" consists of the following fixed-length
76  *      fields from the CREDENTIALS structure (see "krb.h"):
77  *
78  *              char            service[ANAME_SZ]
79  *              char            instance[INST_SZ]
80  *              char            realm[REALM_SZ]
81  *              C_Block         session
82  *              int             lifetime
83  *              int             kvno
84  *              KTEXT_ST        ticket_st
85  *              u_int32_t            issue_date
86  *
87  * Short description of routines:
88  *
89  * tf_init() opens the ticket file and locks it.
90  *
91  * tf_get_pname() returns the principal's name.
92  *
93  * tf_put_pname() writes the principal's name to the ticket file.
94  *
95  * tf_get_pinst() returns the principal's instance (may be null).
96  *
97  * tf_put_pinst() writes the instance.
98  *
99  * tf_get_cred() returns the next CREDENTIALS record.
100  *
101  * tf_save_cred() appends a new CREDENTIAL record to the ticket file.
102  *
103  * tf_close() closes the ticket file and releases the lock.
104  *
105  * tf_gets() returns the next null-terminated string.  It's an internal
106  * routine used by tf_get_pname(), tf_get_pinst(), and tf_get_cred().
107  *
108  * tf_read() reads a given number of bytes.  It's an internal routine
109  * used by tf_get_cred().
110  */
111
112 /*
113  * tf_init() should be called before the other ticket file routines.
114  * It takes the name of the ticket file to use, "tf_name", and a
115  * read/write flag "rw" as arguments. 
116  *
117  * It tries to open the ticket file, checks the mode, and if everything
118  * is okay, locks the file.  If it's opened for reading, the lock is
119  * shared.  If it's opened for writing, the lock is exclusive. 
120  *
121  * Returns KSUCCESS if all went well, otherwise one of the following: 
122  *
123  * NO_TKT_FIL   - file wasn't there
124  * TKT_FIL_ACC  - file was in wrong mode, etc.
125  * TKT_FIL_LCK  - couldn't lock the file, even after a retry
126  */
127
128 #ifdef _NO_LOCKING
129 #undef flock
130 #define flock(F, M) 0
131 #endif
132
133 int
134 tf_init(char *tf_name, int rw)
135 {
136   /* Unix implementation */
137   int wflag;
138   struct stat stat_buf;
139   int i_retry;
140
141   switch (rw) {
142   case R_TKT_FIL:
143     wflag = 0;
144     break;
145   case W_TKT_FIL:
146     wflag = 1;
147     break;
148   default:
149     if (krb_debug)
150       krb_warning("tf_init: illegal parameter\n");
151     return TKT_FIL_ACC;
152   }
153   if (lstat(tf_name, &stat_buf) < 0)
154     switch (errno) {
155     case ENOENT:
156       return NO_TKT_FIL;
157     default:
158       return TKT_FIL_ACC;
159     }
160   if (!S_ISREG(stat_buf.st_mode))
161     return TKT_FIL_ACC;
162
163   /* The code tries to guess when the calling program is running
164    * set-uid and prevent unauthorized access.
165    *
166    * All library functions now assume that the right set of userids
167    * are set upon entry, therefore it's not strictly necessary to
168    * perform these test for programs adhering to these assumptions.
169    *
170    * This doesn't work on cygwin because getuid() returns a different
171    * uid than the owner of files that are created.
172    */
173 #ifndef __CYGWIN__
174   {
175     uid_t me = getuid();
176     if (stat_buf.st_uid != me && me != 0)
177       return TKT_FIL_ACC;
178   }
179 #endif
180
181   /*
182    * If "wflag" is set, open the ticket file in append-writeonly mode
183    * and lock the ticket file in exclusive mode.  If unable to lock
184    * the file, sleep and try again.  If we fail again, return with the
185    * proper error message. 
186    */
187
188   curpos = sizeof(tfbfr);
189
190     
191   if (wflag) {
192     fd = open(tf_name, O_RDWR | O_BINARY, 0600);
193     if (fd < 0) {
194       return TKT_FIL_ACC;
195     }
196     for (i_retry = 0; i_retry < TF_LCK_RETRY_COUNT; i_retry++) {
197       if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
198         if (krb_debug)
199           krb_warning("tf_init: retry %d of write lock of `%s'.\n",
200                       i_retry, tf_name);
201         sleep (TF_LCK_RETRY);
202       } else {
203         return KSUCCESS;                /* all done */
204       }
205     }
206     close (fd);
207     fd = -1;
208     return TKT_FIL_LCK;
209   }
210   /*
211    * Otherwise "wflag" is not set and the ticket file should be opened
212    * for read-only operations and locked for shared access. 
213    */
214
215   fd = open(tf_name, O_RDONLY | O_BINARY, 0600);
216   if (fd < 0) {
217     return TKT_FIL_ACC;
218   }
219
220   for (i_retry = 0; i_retry < TF_LCK_RETRY_COUNT; i_retry++) {
221     if (flock(fd, LOCK_SH | LOCK_NB) < 0) {
222       if (krb_debug)
223         krb_warning("tf_init: retry %d of read lock of `%s'.\n",
224                     i_retry, tf_name);
225       sleep (TF_LCK_RETRY);
226     } else {
227       return KSUCCESS;          /* all done */
228     }
229   }
230   /* failure */
231   close(fd);
232   fd = -1;
233   return TKT_FIL_LCK;
234 }
235
236 /*
237  * tf_create() should be called when creating a new ticket file.
238  * The only argument is the name of the ticket file.
239  * After calling this, it should be possible to use other tf_* functions.
240  *
241  * New algoritm for creating ticket file:
242  * 1. try to erase contents of existing file.
243  * 2. try to remove old file.
244  * 3. try to open with O_CREAT and O_EXCL
245  * 4. if this fails, someone has created a file in between 1 and 2 and
246  *    we should fail.  Otherwise, all is wonderful.
247  */
248
249 int
250 tf_create(char *tf_name)
251 {
252   if (unlink (tf_name) && errno != ENOENT)
253     return TKT_FIL_ACC;
254
255   fd = open(tf_name, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
256   if (fd < 0)
257     return TKT_FIL_ACC;
258   if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
259     sleep(TF_LCK_RETRY);
260     if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
261       close(fd);
262       fd = -1;
263       return TKT_FIL_LCK;
264     }
265   }
266   return KSUCCESS;
267 }
268
269 /*
270  * tf_get_pname() reads the principal's name from the ticket file. It
271  * should only be called after tf_init() has been called.  The
272  * principal's name is filled into the "p" parameter.  If all goes well,
273  * KSUCCESS is returned.  If tf_init() wasn't called, TKT_FIL_INI is
274  * returned.  If the name was null, or EOF was encountered, or the name
275  * was longer than ANAME_SZ, TKT_FIL_FMT is returned. 
276  */
277
278 int
279 tf_get_pname(char *p)
280 {
281   if (fd < 0) {
282     if (krb_debug)
283       krb_warning("tf_get_pname called before tf_init.\n");
284     return TKT_FIL_INI;
285   }
286   if (tf_gets(p, ANAME_SZ) < 2) /* can't be just a null */
287     {
288       if (krb_debug) 
289         krb_warning ("tf_get_pname: pname < 2.\n");
290       return TKT_FIL_FMT;
291     }
292   return KSUCCESS;
293 }
294
295 /*
296  * tf_put_pname() sets the principal's name in the ticket file. Call
297  * after tf_create().
298  */
299
300 int
301 tf_put_pname(const char *p)
302 {
303   unsigned count;
304
305   if (fd < 0) {
306     if (krb_debug)
307       krb_warning("tf_put_pname called before tf_create.\n");
308     return TKT_FIL_INI;
309   }
310   count = strlen(p)+1;
311   if (write(fd,p,count) != count)
312     return(KFAILURE);
313   return KSUCCESS;
314 }
315
316 /*
317  * tf_get_pinst() reads the principal's instance from a ticket file.
318  * It should only be called after tf_init() and tf_get_pname() have been
319  * called.  The instance is filled into the "inst" parameter.  If all
320  * goes well, KSUCCESS is returned.  If tf_init() wasn't called,
321  * TKT_FIL_INI is returned.  If EOF was encountered, or the instance
322  * was longer than ANAME_SZ, TKT_FIL_FMT is returned.  Note that the
323  * instance may be null. 
324  */
325
326 int
327 tf_get_pinst(char *inst)
328 {
329   if (fd < 0) {
330     if (krb_debug)
331       krb_warning("tf_get_pinst called before tf_init.\n");
332     return TKT_FIL_INI;
333   }
334   if (tf_gets(inst, INST_SZ) < 1)
335     {
336       if (krb_debug)
337         krb_warning("tf_get_pinst: inst_sz < 1.\n");
338       return TKT_FIL_FMT;
339     }
340   return KSUCCESS;
341 }
342
343 /*
344  * tf_put_pinst writes the principal's instance to the ticket file.
345  * Call after tf_create.
346  */
347
348 int
349 tf_put_pinst(const char *inst)
350 {
351   unsigned count;
352
353   if (fd < 0) {
354     if (krb_debug)
355       krb_warning("tf_put_pinst called before tf_create.\n");
356     return TKT_FIL_INI;
357   }
358   count = strlen(inst)+1;
359   if (write(fd,inst,count) != count)
360     return(KFAILURE);
361   return KSUCCESS;
362 }
363
364 /*
365  * tf_get_cred() reads a CREDENTIALS record from a ticket file and fills
366  * in the given structure "c".  It should only be called after tf_init(),
367  * tf_get_pname(), and tf_get_pinst() have been called. If all goes well,
368  * KSUCCESS is returned.  Possible error codes are: 
369  *
370  * TKT_FIL_INI  - tf_init wasn't called first
371  * TKT_FIL_FMT  - bad format
372  * EOF          - end of file encountered
373  */
374
375 static int
376 real_tf_get_cred(CREDENTIALS *c)
377 {
378   KTEXT   ticket = &c->ticket_st;       /* pointer to ticket */
379   int     k_errno;
380
381   if (fd < 0) {
382     if (krb_debug)
383       krb_warning ("tf_get_cred called before tf_init.\n");
384     return TKT_FIL_INI;
385   }
386   if ((k_errno = tf_gets(c->service, SNAME_SZ)) < 2)
387     switch (k_errno) {
388     case TOO_BIG:
389       if (krb_debug)
390         krb_warning("tf_get_cred: too big service cred.\n");
391     case 1:             /* can't be just a null */
392       tf_close();
393       if (krb_debug)
394         krb_warning("tf_get_cred: null service cred.\n");
395       return TKT_FIL_FMT;
396     case 0:
397       return EOF;
398     }
399   if ((k_errno = tf_gets(c->instance, INST_SZ)) < 1)
400     switch (k_errno) {
401     case TOO_BIG:
402       if (krb_debug)
403         krb_warning ("tf_get_cred: too big instance cred.\n");
404       return TKT_FIL_FMT;
405     case 0:
406       return EOF;
407     }
408   if ((k_errno = tf_gets(c->realm, REALM_SZ)) < 2)
409     switch (k_errno) {
410     case TOO_BIG:
411       if (krb_debug)
412         krb_warning ("tf_get_cred: too big realm cred.\n");
413     case 1:             /* can't be just a null */
414       tf_close();
415       if (krb_debug)
416         krb_warning ("tf_get_cred: null realm cred.\n");
417       return TKT_FIL_FMT;
418     case 0:
419       return EOF;
420     }
421   if (
422       tf_read((c->session), DES_KEY_SZ) < 1 ||
423       tf_read(&(c->lifetime), sizeof(c->lifetime)) < 1 ||
424       tf_read(&(c->kvno), sizeof(c->kvno)) < 1 ||
425       tf_read(&(ticket->length), sizeof(ticket->length))
426       < 1 ||
427       /* don't try to read a silly amount into ticket->dat */
428       ticket->length > MAX_KTXT_LEN ||
429       tf_read((ticket->dat), ticket->length) < 1 ||
430       tf_read(&(c->issue_date), sizeof(c->issue_date)) < 1
431       ) {
432     tf_close();
433     if (krb_debug)
434       krb_warning ("tf_get_cred: failed tf_read.\n");
435     return TKT_FIL_FMT;
436   }
437   return KSUCCESS;
438 }
439
440 int
441 tf_get_cred(CREDENTIALS *c)
442 {
443   int ret;
444   int fake;
445
446   do {
447     fake = 0;
448
449     ret = real_tf_get_cred (c);
450     if (ret)
451       return ret;
452
453     if(strcmp(c->service, MAGIC_TICKET_NAME) == 0) {
454       if(strcmp(c->instance, MAGIC_TICKET_TIME_DIFF_INST) == 0) {
455         /* we found the magic `time diff' ticket; update the kdc time
456          differential, and then get the next ticket */
457         u_int32_t d;
458
459         krb_get_int(c->ticket_st.dat, &d, 4, 0);
460         krb_set_kdc_time_diff(d);
461         fake = 1;
462       } else if (strcmp(c->instance, MAGIC_TICKET_ADDR_INST) == 0) {
463         fake = 1;
464       }
465     }
466   } while (fake);
467   return ret;
468 }
469
470 int
471 tf_get_cred_addr(char *realm, size_t realm_sz, struct in_addr *addr)
472 {
473   int ret;
474   int fake;
475   CREDENTIALS cred;
476
477   do {
478     fake = 1;
479
480     ret = real_tf_get_cred (&cred);
481     if (ret)
482       return ret;
483
484     if(strcmp(cred.service, MAGIC_TICKET_NAME) == 0) {
485       if(strcmp(cred.instance, MAGIC_TICKET_TIME_DIFF_INST) == 0) {
486         /* we found the magic `time diff' ticket; update the kdc time
487          differential, and then get the next ticket */
488         u_int32_t d;
489
490         krb_get_int(cred.ticket_st.dat, &d, 4, 0);
491         krb_set_kdc_time_diff(d);
492       } else if (strcmp(cred.instance, MAGIC_TICKET_ADDR_INST) == 0) {
493         strlcpy(realm, cred.realm, realm_sz);
494         memcpy (addr, cred.ticket_st.dat, sizeof(*addr));
495         fake = 0;
496       }
497     }
498   } while (fake);
499   return ret;
500 }
501
502 /*
503  * tf_close() closes the ticket file and sets "fd" to -1. If "fd" is
504  * not a valid file descriptor, it just returns.  It also clears the
505  * buffer used to read tickets.
506  *
507  * The return value is not defined.
508  */
509
510 void
511 tf_close(void)
512 {
513   if (!(fd < 0)) {
514     flock(fd, LOCK_UN);
515     close(fd);
516     fd = -1;            /* see declaration of fd above */
517   }
518   memset(tfbfr, 0, sizeof(tfbfr));
519 }
520
521 /*
522  * tf_gets() is an internal routine.  It takes a string "s" and a count
523  * "n", and reads from the file until either it has read "n" characters,
524  * or until it reads a null byte. When finished, what has been read exists
525  * in "s". If it encounters EOF or an error, it closes the ticket file. 
526  *
527  * Possible return values are:
528  *
529  * n            the number of bytes read (including null terminator)
530  *              when all goes well
531  *
532  * 0            end of file or read error
533  *
534  * TOO_BIG      if "count" characters are read and no null is
535  *              encountered. This is an indication that the ticket
536  *              file is seriously ill.
537  */
538
539 static int
540 tf_gets(char *s, int n)
541 {
542   int count;
543
544   if (fd < 0) {
545     if (krb_debug)
546       krb_warning ("tf_gets called before tf_init.\n");
547     return TKT_FIL_INI;
548   }
549   for (count = n - 1; count > 0; --count) {
550     if (curpos >= sizeof(tfbfr)) {
551       lastpos = read(fd, tfbfr, sizeof(tfbfr));
552       curpos = 0;
553     }
554     if (curpos == lastpos) {
555       tf_close();
556       return 0;
557     }
558     *s = tfbfr[curpos++];
559     if (*s++ == '\0')
560       return (n - count);
561   }
562   tf_close();
563   return TOO_BIG;
564 }
565
566 /*
567  * tf_read() is an internal routine.  It takes a string "s" and a count
568  * "n", and reads from the file until "n" bytes have been read.  When
569  * finished, what has been read exists in "s".  If it encounters EOF or
570  * an error, it closes the ticket file.
571  *
572  * Possible return values are:
573  *
574  * n            the number of bytes read when all goes well
575  *
576  * 0            on end of file or read error
577  */
578
579 static int
580 tf_read(void *v, int n)
581 {
582   char *s = (char *)v;
583   int count;
584     
585   for (count = n; count > 0; --count) {
586     if (curpos >= sizeof(tfbfr)) {
587       lastpos = read(fd, tfbfr, sizeof(tfbfr));
588       curpos = 0;
589     }
590     if (curpos == lastpos) {
591       tf_close();
592       return 0;
593     }
594     *s++ = tfbfr[curpos++];
595   }
596   return n;
597 }
598      
599 /*
600  * tf_save_cred() appends an incoming ticket to the end of the ticket
601  * file.  You must call tf_init() before calling tf_save_cred().
602  *
603  * The "service", "instance", and "realm" arguments specify the
604  * server's name; "session" contains the session key to be used with
605  * the ticket; "kvno" is the server key version number in which the
606  * ticket is encrypted, "ticket" contains the actual ticket, and
607  * "issue_date" is the time the ticket was requested (local host's time).
608  *
609  * Returns KSUCCESS if all goes well, TKT_FIL_INI if tf_init() wasn't
610  * called previously, and KFAILURE for anything else that went wrong.
611  */
612  
613 int
614 tf_save_cred(char *service,     /* Service name */
615              char *instance,    /* Instance */
616              char *realm,       /* Auth domain */
617              unsigned char *session, /* Session key */
618              int lifetime,      /* Lifetime */
619              int kvno,          /* Key version number */
620              KTEXT ticket,      /* The ticket itself */
621              u_int32_t issue_date) /* The issue time */
622 {
623   int count;                    /* count for write */
624
625   if (fd < 0) {                 /* fd is ticket file as set by tf_init */
626     if (krb_debug)
627       krb_warning ("tf_save_cred called before tf_init.\n");
628     return TKT_FIL_INI;
629   }
630   /* Find the end of the ticket file */
631   lseek(fd, 0L, SEEK_END);
632
633   /* Write the ticket and associated data */
634   /* Service */
635   count = strlen(service) + 1;
636   if (write(fd, service, count) != count)
637     goto bad;
638   /* Instance */
639   count = strlen(instance) + 1;
640   if (write(fd, instance, count) != count)
641     goto bad;
642   /* Realm */
643   count = strlen(realm) + 1;
644   if (write(fd, realm, count) != count)
645     goto bad;
646   /* Session key */
647   if (write(fd, session, 8) != 8)
648     goto bad;
649   /* Lifetime */
650   if (write(fd, &lifetime, sizeof(int)) != sizeof(int))
651     goto bad;
652   /* Key vno */
653   if (write(fd, &kvno, sizeof(int)) != sizeof(int))
654     goto bad;
655   /* Tkt length */
656   if (write(fd, &(ticket->length), sizeof(int)) !=
657       sizeof(int))
658     goto bad;
659   /* Ticket */
660   count = ticket->length;
661   if (write(fd, ticket->dat, count) != count)
662     goto bad;
663   /* Issue date */
664   if (write(fd, &issue_date, sizeof(issue_date)) != sizeof(issue_date))
665     goto bad;
666
667   return (KSUCCESS);
668 bad:
669   return (KFAILURE);
670 }
671
672 int
673 tf_setup(CREDENTIALS *cred, const char *pname, const char *pinst)
674 {
675     int ret;
676     ret = tf_create(tkt_string());
677     if (ret != KSUCCESS)
678         return ret;
679
680     if (tf_put_pname(pname) != KSUCCESS ||
681         tf_put_pinst(pinst) != KSUCCESS) {
682         tf_close();
683         return INTK_ERR;
684     }
685     
686     if(krb_get_kdc_time_diff() != 0) {
687         /* Add an extra magic ticket containing the time differential
688            to the kdc. The first ticket defines which realm we belong
689            to, but since this ticket gets the same realm as the tgt,
690            this shouldn't be a problem */
691         des_cblock s = { 0, 0, 0, 0, 0, 0, 0, 0 };
692         KTEXT_ST t;
693         int d = krb_get_kdc_time_diff();
694         krb_put_int(d, t.dat, sizeof(t.dat), 4);
695         t.length = 4;
696         tf_save_cred(MAGIC_TICKET_NAME, MAGIC_TICKET_TIME_DIFF_INST,
697                      cred->realm, s, 
698                      cred->lifetime, 0, &t, cred->issue_date);
699     }
700     ret = tf_save_cred(cred->service, cred->instance, cred->realm, 
701                        cred->session, cred->lifetime, cred->kvno,
702                        &cred->ticket_st, cred->issue_date);
703     tf_close();
704     return ret;
705 }
706
707 int
708 in_tkt(char *pname, char *pinst)
709 {
710   int ret;
711   
712   ret = tf_create (tkt_string());
713   if (ret != KSUCCESS)
714     return ret;
715
716     if (tf_put_pname(pname) != KSUCCESS ||
717         tf_put_pinst(pinst) != KSUCCESS) {
718         tf_close();
719         return INTK_ERR;
720     }
721
722     tf_close();
723     return KSUCCESS;
724 }
725
726 /*
727  * If there's a magic ticket with an address for realm `realm' in
728  * ticket file, return it in `addr'.
729  * realm == NULL means any realm.
730  */
731
732 int
733 tf_get_addr (const char *realm, struct in_addr *addr)
734 {
735   CREDENTIALS cred;
736   krb_principal princ;
737   int ret;
738
739   ret = tf_init (tkt_string (), R_TKT_FIL);
740   if (ret)
741     return ret;
742
743   ret = tf_get_pname (princ.name);
744   if (ret)
745     goto out;
746   ret = tf_get_pinst (princ.name);
747   if (ret)
748     goto out;
749   while ((ret = real_tf_get_cred (&cred)) == KSUCCESS) {
750     if (strcmp (cred.service, MAGIC_TICKET_NAME) == 0
751         && strcmp (cred.instance, MAGIC_TICKET_ADDR_INST) == 0
752         && (realm == NULL
753             || strcmp (cred.realm, realm) == 0)) {
754       memcpy (addr, cred.ticket_st.dat, sizeof(*addr));
755       goto out;
756     }
757   }
758   ret = KFAILURE;
759
760 out:
761   tf_close ();
762   return ret;
763 }
764
765 /*
766  * Store `realm, addr' as a magic ticket.
767  */
768
769 int
770 tf_store_addr (const char *realm, struct in_addr *addr)
771 {
772   CREDENTIALS c;
773   krb_principal princ;
774   int ret;
775   des_cblock s = { 0, 0, 0, 0, 0, 0, 0, 0 };
776   KTEXT_ST t;
777
778   ret = tf_init (tkt_string (), W_TKT_FIL);
779   if (ret)
780     return ret;
781
782   t.length = sizeof(*addr);
783   memcpy (t.dat, addr, sizeof(*addr));
784
785   ret = tf_save_cred (MAGIC_TICKET_NAME, MAGIC_TICKET_ADDR_INST,
786                       (char *)realm, s, 0, /* lifetime */ 
787                       0, /* kvno */
788                       &t, time(NULL));
789   tf_close ();
790   return ret;
791 }