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