Merge from vendor branch HEIMDAL:
[dragonfly.git] / crypto / heimdal-0.6.3 / appl / ftp / ftpd / kauth.c
1 /*
2  * Copyright (c) 1995, 1996, 1997, 1998 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 "ftpd_locl.h"
35
36 RCSID("$Id: kauth.c,v 1.25 1999/12/02 16:58:31 joda Exp $");
37
38 static KTEXT_ST cip;
39 static unsigned int lifetime;
40 static time_t local_time;
41
42 static krb_principal pr;
43
44 static int do_destroy_tickets = 1;
45
46 static int
47 save_tkt(const char *user,
48          const char *instance,
49          const char *realm,
50          const void *arg, 
51          key_proc_t key_proc,
52          KTEXT *cipp)
53 {
54     local_time = time(0);
55     memmove(&cip, *cipp, sizeof(cip));
56     return -1;
57 }
58
59 static int
60 store_ticket(KTEXT cip)
61 {
62     char *ptr;
63     des_cblock session;
64     krb_principal sp;
65     unsigned char kvno;
66     KTEXT_ST tkt;
67     int left = cip->length;
68     int len;
69     int kerror;
70     
71     ptr = (char *) cip->dat;
72
73     /* extract session key */
74     memmove(session, ptr, 8);
75     ptr += 8;
76     left -= 8;
77
78     len = strnlen(ptr, left);
79     if (len == left)
80         return(INTK_BADPW);
81     
82     /* extract server's name */
83     strlcpy(sp.name, ptr, sizeof(sp.name));
84     ptr += len + 1;
85     left -= len + 1;
86
87     len = strnlen(ptr, left);
88     if (len == left)
89         return(INTK_BADPW);
90     
91     /* extract server's instance */
92     strlcpy(sp.instance, ptr, sizeof(sp.instance));
93     ptr += len + 1;
94     left -= len + 1;
95
96     len = strnlen(ptr, left);
97     if (len == left)
98         return(INTK_BADPW);
99     
100     /* extract server's realm */
101     strlcpy(sp.realm, ptr, sizeof(sp.realm));
102     ptr += len + 1;
103     left -= len + 1;
104
105     if(left < 3)
106         return INTK_BADPW;
107     /* extract ticket lifetime, server key version, ticket length */
108     /* be sure to avoid sign extension on lifetime! */
109     lifetime = (unsigned char) ptr[0];
110     kvno = (unsigned char) ptr[1];
111     tkt.length = (unsigned char) ptr[2];
112     ptr += 3;
113     left -= 3;
114     
115     if (tkt.length > left)
116         return(INTK_BADPW);
117
118     /* extract ticket itself */
119     memmove(tkt.dat, ptr, tkt.length);
120     ptr += tkt.length;
121     left -= tkt.length;
122
123     /* Here is where the time should be verified against the KDC.
124      * Unfortunately everything is sent in host byte order (receiver
125      * makes wrong) , and at this stage there is no way for us to know
126      * which byteorder the KDC has. So we simply ignore the time,
127      * there are no security risks with this, the only thing that can
128      * happen is that we might receive a replayed ticket, which could
129      * at most be useless.
130      */
131     
132 #if 0
133     /* check KDC time stamp */
134     {
135         time_t kdc_time;
136
137         memmove(&kdc_time, ptr, sizeof(kdc_time));
138         if (swap_bytes) swap_u_long(kdc_time);
139
140         ptr += 4;
141     
142         if (abs((int)(local_time - kdc_time)) > CLOCK_SKEW) {
143             return(RD_AP_TIME);         /* XXX should probably be better
144                                            code */
145         }
146     }
147 #endif
148
149     /* initialize ticket cache */
150
151     if (tf_create(TKT_FILE) != KSUCCESS)
152         return(INTK_ERR);
153
154     if (tf_put_pname(pr.name) != KSUCCESS ||
155         tf_put_pinst(pr.instance) != KSUCCESS) {
156         tf_close();
157         return(INTK_ERR);
158     }
159
160     
161     kerror = tf_save_cred(sp.name, sp.instance, sp.realm, session, 
162                           lifetime, kvno, &tkt, local_time);
163     tf_close();
164
165     return(kerror);
166 }
167
168 void
169 kauth(char *principal, char *ticket)
170 {
171     char *p;
172     int ret;
173   
174     if(get_command_prot() != prot_private) {
175         reply(500, "Request denied (bad protection level)");
176         return;
177     }
178     ret = krb_parse_name(principal, &pr);
179     if(ret){
180         reply(500, "Bad principal: %s.", krb_get_err_text(ret));
181         return;
182     }
183     if(pr.realm[0] == 0)
184         krb_get_lrealm(pr.realm, 1);
185
186     if(ticket){
187         cip.length = base64_decode(ticket, &cip.dat);
188         if(cip.length == -1){
189             reply(500, "Failed to decode data.");
190             return;
191         }
192         ret = store_ticket(&cip);
193         if(ret){
194             reply(500, "Kerberos error: %s.", krb_get_err_text(ret));
195             memset(&cip, 0, sizeof(cip));
196             return;
197         }
198         do_destroy_tickets = 1;
199
200         if(k_hasafs())
201             krb_afslog(0, 0);
202         reply(200, "Tickets will be destroyed on exit.");
203         return;
204     }
205     
206     ret = krb_get_in_tkt (pr.name,
207                           pr.instance,
208                           pr.realm,
209                           KRB_TICKET_GRANTING_TICKET,
210                           pr.realm,
211                           DEFAULT_TKT_LIFE,
212                           NULL, save_tkt, NULL);
213     if(ret != INTK_BADPW){
214         reply(500, "Kerberos error: %s.", krb_get_err_text(ret));
215         return;
216     }
217     if(base64_encode(cip.dat, cip.length, &p) < 0) {
218         reply(500, "Out of memory while base64-encoding.");
219         return;
220     }
221     reply(300, "P=%s T=%s", krb_unparse_name(&pr), p);
222     free(p);
223     memset(&cip, 0, sizeof(cip));
224 }
225
226
227 static char *
228 short_date(int32_t dp)
229 {
230     char *cp;
231     time_t t = (time_t)dp;
232
233     if (t == (time_t)(-1L)) return "***  Never  *** ";
234     cp = ctime(&t) + 4;
235     cp[15] = '\0';
236     return (cp);
237 }
238
239 void
240 klist(void)
241 {
242     int err;
243
244     char *file = tkt_string();
245
246     krb_principal pr;
247     
248     char buf1[128], buf2[128];
249     int header = 1;
250     CREDENTIALS c;
251
252     
253
254     err = tf_init(file, R_TKT_FIL);
255     if(err != KSUCCESS){
256         reply(500, "%s", krb_get_err_text(err));
257         return;
258     }
259     tf_close();
260
261     /* 
262      * We must find the realm of the ticket file here before calling
263      * tf_init because since the realm of the ticket file is not
264      * really stored in the principal section of the file, the
265      * routine we use must itself call tf_init and tf_close.
266      */
267     err = krb_get_tf_realm(file, pr.realm);
268     if(err != KSUCCESS){
269         reply(500, "%s", krb_get_err_text(err));
270         return;
271     }
272
273     err = tf_init(file, R_TKT_FIL);
274     if(err != KSUCCESS){
275         reply(500, "%s", krb_get_err_text(err));
276         return;
277     }
278
279     err = tf_get_pname(pr.name);
280     if(err != KSUCCESS){
281         reply(500, "%s", krb_get_err_text(err));
282         return;
283     }
284     err = tf_get_pinst(pr.instance);
285     if(err != KSUCCESS){
286         reply(500, "%s", krb_get_err_text(err));
287         return;
288     }
289
290     /* 
291      * You may think that this is the obvious place to get the
292      * realm of the ticket file, but it can't be done here as the
293      * routine to do this must open the ticket file.  This is why 
294      * it was done before tf_init.
295      */
296        
297     lreply(200, "Ticket file: %s", tkt_string());
298
299     lreply(200, "Principal: %s", krb_unparse_name(&pr));
300     while ((err = tf_get_cred(&c)) == KSUCCESS) {
301         if (header) {
302             lreply(200, "%-15s  %-15s  %s",
303                    "  Issued", "  Expires", "  Principal (kvno)");
304             header = 0;
305         }
306         strlcpy(buf1, short_date(c.issue_date), sizeof(buf1));
307         c.issue_date = krb_life_to_time(c.issue_date, c.lifetime);
308         if (time(0) < (unsigned long) c.issue_date)
309             strlcpy(buf2, short_date(c.issue_date), sizeof(buf2));
310         else
311             strlcpy(buf2, ">>> Expired <<< ", sizeof(buf2));
312         lreply(200, "%s  %s  %s (%d)", buf1, buf2,
313                krb_unparse_name_long(c.service, c.instance, c.realm), c.kvno); 
314     }
315     if (header && err == EOF) {
316         lreply(200, "No tickets in file.");
317     }
318     reply(200, " ");
319 }
320
321 /*
322  * Only destroy if we created the tickets
323  */
324
325 void
326 cond_kdestroy(void)
327 {
328     if (do_destroy_tickets)
329         dest_tkt();
330     afsunlog();
331 }
332
333 void
334 kdestroy(void)
335 {
336     dest_tkt();
337     afsunlog();
338     reply(200, "Tickets destroyed");
339 }
340
341 void
342 krbtkfile(const char *tkfile)
343 {
344     do_destroy_tickets = 0;
345     krb_set_tkt_string(tkfile);
346     reply(200, "Using ticket file %s", tkfile);
347 }
348
349 void
350 afslog(const char *cell)
351 {
352     if(k_hasafs()) {
353         krb_afslog(cell, 0);
354         reply(200, "afslog done");
355     } else {
356         reply(200, "no AFS present");
357     }
358 }
359
360 void
361 afsunlog(void)
362 {
363     if(k_hasafs())
364         k_unlog();
365 }