Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / lib / krb / get_ad_tkt.c
1 /*
2  * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "krb_locl.h"
35
36 RCSID("$Id: get_ad_tkt.c,v 1.22 1999/12/02 16:58:41 joda Exp $");
37
38 /*
39  * get_ad_tkt obtains a new service ticket from Kerberos, using
40  * the ticket-granting ticket which must be in the ticket file.
41  * It is typically called by krb_mk_req() when the client side
42  * of an application is creating authentication information to be
43  * sent to the server side.
44  *
45  * get_ad_tkt takes four arguments: three pointers to strings which
46  * contain the name, instance, and realm of the service for which the
47  * ticket is to be obtained; and an integer indicating the desired
48  * lifetime of the ticket.
49  *
50  * It returns an error status if the ticket couldn't be obtained,
51  * or AD_OK if all went well.  The ticket is stored in the ticket
52  * cache.
53  *
54  * The request sent to the Kerberos ticket-granting service looks
55  * like this:
56  *
57  * pkt->dat
58  *
59  * TEXT                 original contents of    authenticator+ticket
60  *                      pkt->dat                built in krb_mk_req call
61  * 
62  * 4 bytes              time_ws                 always 0 (?)
63  * char                 lifetime                lifetime argument passed
64  * string               service                 service name argument
65  * string               sinstance               service instance arg.
66  *
67  * See "prot.h" for the reply packet layout and definitions of the
68  * extraction macros like pkt_version(), pkt_msg_type(), etc.
69  */
70
71 int
72 get_ad_tkt(char *service, char *sinstance, char *realm, int lifetime)
73 {
74     static KTEXT_ST pkt_st;
75     KTEXT pkt = & pkt_st;       /* Packet to KDC */
76     static KTEXT_ST rpkt_st;
77     KTEXT rpkt = &rpkt_st;      /* Returned packet */
78
79     CREDENTIALS cr;
80     char lrealm[REALM_SZ];
81     u_int32_t time_ws = 0;
82     int kerror;
83     unsigned char *p;
84     size_t rem;
85     int tmp;
86
87     /*
88      * First check if we have a "real" TGT for the corresponding
89      * realm, if we don't, use ordinary inter-realm authentication.
90      */
91
92     kerror = krb_get_cred(KRB_TICKET_GRANTING_TICKET, realm, realm, &cr);
93     if (kerror == KSUCCESS) {
94       strlcpy(lrealm, realm, REALM_SZ);
95     } else
96       kerror = krb_get_tf_realm(TKT_FILE, lrealm);
97     
98     if (kerror != KSUCCESS)
99         return(kerror);
100
101     /*
102      * Look for the session key (and other stuff we don't need)
103      * in the ticket file for krbtgt.realm@lrealm where "realm" 
104      * is the service's realm (passed in "realm" argument) and 
105      * lrealm is the realm of our initial ticket.  If we don't 
106      * have this, we will try to get it.
107      */
108     
109     if ((kerror = krb_get_cred(KRB_TICKET_GRANTING_TICKET,
110                                realm, lrealm, &cr)) != KSUCCESS) {
111         /*
112          * If realm == lrealm, we have no hope, so let's not even try.
113          */
114         if ((strncmp(realm, lrealm, REALM_SZ)) == 0)
115             return(AD_NOTGT);
116         else{
117             if ((kerror = 
118                  get_ad_tkt(KRB_TICKET_GRANTING_TICKET,
119                             realm, lrealm, lifetime)) != KSUCCESS) {
120                 if (kerror == KDC_PR_UNKNOWN)
121                   return(AD_INTR_RLM_NOTGT);
122                 else
123                   return(kerror);
124             }
125             if ((kerror = krb_get_cred(KRB_TICKET_GRANTING_TICKET,
126                                        realm, lrealm, &cr)) != KSUCCESS)
127                 return(kerror);
128         }
129     }
130     
131     /*
132      * Make up a request packet to the "krbtgt.realm@lrealm".
133      * Start by calling krb_mk_req() which puts ticket+authenticator
134      * into "pkt".  Then tack other stuff on the end.
135      */
136     
137     kerror = krb_mk_req(pkt,
138                         KRB_TICKET_GRANTING_TICKET,
139                         realm,lrealm,0L);
140
141     if (kerror)
142         return(AD_NOTGT);
143
144     p = pkt->dat + pkt->length;
145     rem = sizeof(pkt->dat) - pkt->length;
146
147     tmp = krb_put_int(time_ws, p, rem, 4);
148     if (tmp < 0)
149         return KFAILURE;
150     p += tmp;
151     rem -= tmp;
152
153     tmp = krb_put_int(lifetime, p, rem, 1);
154     if (tmp < 0)
155         return KFAILURE;
156     p += tmp;
157     rem -= tmp;
158
159     tmp = krb_put_nir(service, sinstance, NULL, p, rem);
160     if (tmp < 0)
161         return KFAILURE;
162     p += tmp;
163     rem -= tmp;
164     
165     pkt->length = p - pkt->dat;
166     rpkt->length = 0;
167     
168     /* Send the request to the local ticket-granting server */
169     if ((kerror = send_to_kdc(pkt, rpkt, realm))) return(kerror);
170
171     /* check packet version of the returned packet */
172
173     {
174         KTEXT_ST cip;
175         CREDENTIALS cred;
176         struct timeval tv;
177
178         kerror = kdc_reply_cipher(rpkt, &cip);
179         if(kerror != KSUCCESS)
180             return kerror;
181         
182         encrypt_ktext(&cip, &cr.session, DES_DECRYPT);
183
184         kerror = kdc_reply_cred(&cip, &cred);
185         if(kerror != KSUCCESS)
186             return kerror;
187
188         if (strcmp(cred.service, service) || strcmp(cred.instance, sinstance) ||
189             strcmp(cred.realm, realm))  /* not what we asked for */
190             return INTK_ERR;    /* we need a better code here XXX */
191         
192         krb_kdctimeofday(&tv);
193         if (abs((int)(tv.tv_sec - cred.issue_date)) > CLOCK_SKEW) {
194             return RD_AP_TIME; /* XXX should probably be better code */
195         }
196         
197
198         kerror = save_credentials(cred.service, cred.instance, cred.realm, 
199                                   cred.session, cred.lifetime, cred.kvno, 
200                                   &cred.ticket_st, tv.tv_sec);
201         return kerror;
202     }
203 }