xref: /freebsd/crypto/heimdal/appl/ftp/ftpd/kauth.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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 }
366