security.c revision 178825
1178825Sdfr/*
2178825Sdfr * Copyright (c) 1998-2002, 2005 Kungliga Tekniska H�gskolan
3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * All rights reserved.
5178825Sdfr *
6178825Sdfr * Redistribution and use in source and binary forms, with or without
7178825Sdfr * modification, are permitted provided that the following conditions
8178825Sdfr * are met:
9178825Sdfr *
10178825Sdfr * 1. Redistributions of source code must retain the above copyright
11178825Sdfr *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
14178825Sdfr *    notice, this list of conditions and the following disclaimer in the
15178825Sdfr *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors
18178825Sdfr *    may be used to endorse or promote products derived from this software
19178825Sdfr *    without specific prior written permission.
20178825Sdfr *
21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31178825Sdfr * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#ifdef FTP_SERVER
35178825Sdfr#include "ftpd_locl.h"
36178825Sdfr#else
37178825Sdfr#include "ftp_locl.h"
38178825Sdfr#endif
39178825Sdfr
40178825SdfrRCSID("$Id: security.c 21225 2007-06-20 10:16:02Z lha $");
41178825Sdfr
42178825Sdfrstatic enum protection_level command_prot;
43178825Sdfrstatic enum protection_level data_prot;
44178825Sdfrstatic size_t buffer_size;
45178825Sdfr
46178825Sdfrstruct buffer {
47178825Sdfr    void *data;
48178825Sdfr    size_t size;
49178825Sdfr    size_t index;
50178825Sdfr    int eof_flag;
51178825Sdfr};
52178825Sdfr
53178825Sdfrstatic struct buffer in_buffer, out_buffer;
54178825Sdfrint sec_complete;
55178825Sdfr
56178825Sdfrstatic struct {
57178825Sdfr    enum protection_level level;
58178825Sdfr    const char *name;
59178825Sdfr} level_names[] = {
60178825Sdfr    { prot_clear, "clear" },
61178825Sdfr    { prot_safe, "safe" },
62178825Sdfr    { prot_confidential, "confidential" },
63178825Sdfr    { prot_private, "private" }
64178825Sdfr};
65178825Sdfr
66178825Sdfrstatic const char *
67178825Sdfrlevel_to_name(enum protection_level level)
68178825Sdfr{
69178825Sdfr    int i;
70178825Sdfr    for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
71178825Sdfr	if(level_names[i].level == level)
72178825Sdfr	    return level_names[i].name;
73178825Sdfr    return "unknown";
74178825Sdfr}
75178825Sdfr
76178825Sdfr#ifndef FTP_SERVER /* not used in server */
77178825Sdfrstatic enum protection_level
78178825Sdfrname_to_level(const char *name)
79178825Sdfr{
80178825Sdfr    int i;
81178825Sdfr    for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
82178825Sdfr	if(!strncasecmp(level_names[i].name, name, strlen(name)))
83178825Sdfr	    return level_names[i].level;
84178825Sdfr    return (enum protection_level)-1;
85178825Sdfr}
86178825Sdfr#endif
87178825Sdfr
88178825Sdfr#ifdef FTP_SERVER
89178825Sdfr
90178825Sdfrstatic struct sec_server_mech *mechs[] = {
91178825Sdfr#ifdef KRB5
92178825Sdfr    &gss_server_mech,
93178825Sdfr#endif
94178825Sdfr#ifdef KRB4
95178825Sdfr    &krb4_server_mech,
96178825Sdfr#endif
97178825Sdfr    NULL
98178825Sdfr};
99178825Sdfr
100178825Sdfrstatic struct sec_server_mech *mech;
101178825Sdfr
102178825Sdfr#else
103178825Sdfr
104178825Sdfrstatic struct sec_client_mech *mechs[] = {
105178825Sdfr#ifdef KRB5
106178825Sdfr    &gss_client_mech,
107178825Sdfr#endif
108178825Sdfr#ifdef KRB4
109178825Sdfr    &krb4_client_mech,
110178825Sdfr#endif
111178825Sdfr    NULL
112178825Sdfr};
113178825Sdfr
114178825Sdfrstatic struct sec_client_mech *mech;
115178825Sdfr
116178825Sdfr#endif
117178825Sdfr
118178825Sdfrstatic void *app_data;
119178825Sdfr
120178825Sdfrint
121178825Sdfrsec_getc(FILE *F)
122178825Sdfr{
123178825Sdfr    if(sec_complete && data_prot) {
124178825Sdfr	char c;
125178825Sdfr	if(sec_read(fileno(F), &c, 1) <= 0)
126178825Sdfr	    return EOF;
127178825Sdfr	return c;
128178825Sdfr    } else
129178825Sdfr	return getc(F);
130178825Sdfr}
131178825Sdfr
132178825Sdfrstatic int
133178825Sdfrblock_read(int fd, void *buf, size_t len)
134178825Sdfr{
135178825Sdfr    unsigned char *p = buf;
136178825Sdfr    int b;
137178825Sdfr    while(len) {
138178825Sdfr	b = read(fd, p, len);
139178825Sdfr	if (b == 0)
140178825Sdfr	    return 0;
141178825Sdfr	else if (b < 0)
142178825Sdfr	    return -1;
143178825Sdfr	len -= b;
144178825Sdfr	p += b;
145178825Sdfr    }
146178825Sdfr    return p - (unsigned char*)buf;
147178825Sdfr}
148178825Sdfr
149178825Sdfrstatic int
150178825Sdfrblock_write(int fd, void *buf, size_t len)
151178825Sdfr{
152178825Sdfr    unsigned char *p = buf;
153178825Sdfr    int b;
154178825Sdfr    while(len) {
155178825Sdfr	b = write(fd, p, len);
156178825Sdfr	if(b < 0)
157178825Sdfr	    return -1;
158178825Sdfr	len -= b;
159178825Sdfr	p += b;
160178825Sdfr    }
161178825Sdfr    return p - (unsigned char*)buf;
162178825Sdfr}
163178825Sdfr
164178825Sdfrstatic int
165178825Sdfrsec_get_data(int fd, struct buffer *buf, int level)
166178825Sdfr{
167178825Sdfr    int len;
168178825Sdfr    int b;
169178825Sdfr    void *tmp;
170178825Sdfr
171178825Sdfr    b = block_read(fd, &len, sizeof(len));
172178825Sdfr    if (b == 0)
173178825Sdfr	return 0;
174178825Sdfr    else if (b < 0)
175178825Sdfr	return -1;
176178825Sdfr    len = ntohl(len);
177178825Sdfr    tmp = realloc(buf->data, len);
178178825Sdfr    if (tmp == NULL)
179178825Sdfr	return -1;
180178825Sdfr    buf->data = tmp;
181178825Sdfr    b = block_read(fd, buf->data, len);
182178825Sdfr    if (b == 0)
183178825Sdfr	return 0;
184178825Sdfr    else if (b < 0)
185178825Sdfr	return -1;
186178825Sdfr    buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
187178825Sdfr    buf->index = 0;
188178825Sdfr    return 0;
189178825Sdfr}
190178825Sdfr
191178825Sdfrstatic size_t
192178825Sdfrbuffer_read(struct buffer *buf, void *dataptr, size_t len)
193178825Sdfr{
194178825Sdfr    len = min(len, buf->size - buf->index);
195178825Sdfr    memcpy(dataptr, (char*)buf->data + buf->index, len);
196178825Sdfr    buf->index += len;
197178825Sdfr    return len;
198178825Sdfr}
199178825Sdfr
200178825Sdfrstatic size_t
201178825Sdfrbuffer_write(struct buffer *buf, void *dataptr, size_t len)
202178825Sdfr{
203178825Sdfr    if(buf->index + len > buf->size) {
204178825Sdfr	void *tmp;
205178825Sdfr	if(buf->data == NULL)
206178825Sdfr	    tmp = malloc(1024);
207178825Sdfr	else
208178825Sdfr	    tmp = realloc(buf->data, buf->index + len);
209178825Sdfr	if(tmp == NULL)
210178825Sdfr	    return -1;
211178825Sdfr	buf->data = tmp;
212178825Sdfr	buf->size = buf->index + len;
213178825Sdfr    }
214178825Sdfr    memcpy((char*)buf->data + buf->index, dataptr, len);
215178825Sdfr    buf->index += len;
216178825Sdfr    return len;
217178825Sdfr}
218178825Sdfr
219178825Sdfrint
220178825Sdfrsec_read(int fd, void *dataptr, int length)
221178825Sdfr{
222178825Sdfr    size_t len;
223178825Sdfr    int rx = 0;
224178825Sdfr
225178825Sdfr    if(sec_complete == 0 || data_prot == 0)
226178825Sdfr	return read(fd, dataptr, length);
227178825Sdfr
228178825Sdfr    if(in_buffer.eof_flag){
229178825Sdfr	in_buffer.eof_flag = 0;
230178825Sdfr	return 0;
231178825Sdfr    }
232178825Sdfr
233178825Sdfr    len = buffer_read(&in_buffer, dataptr, length);
234178825Sdfr    length -= len;
235178825Sdfr    rx += len;
236178825Sdfr    dataptr = (char*)dataptr + len;
237178825Sdfr
238178825Sdfr    while(length){
239178825Sdfr	int ret;
240178825Sdfr
241178825Sdfr	ret = sec_get_data(fd, &in_buffer, data_prot);
242178825Sdfr	if (ret < 0)
243178825Sdfr	    return -1;
244178825Sdfr	if(ret == 0 && in_buffer.size == 0) {
245178825Sdfr	    if(rx)
246178825Sdfr		in_buffer.eof_flag = 1;
247178825Sdfr	    return rx;
248178825Sdfr	}
249178825Sdfr	len = buffer_read(&in_buffer, dataptr, length);
250178825Sdfr	length -= len;
251178825Sdfr	rx += len;
252178825Sdfr	dataptr = (char*)dataptr + len;
253178825Sdfr    }
254178825Sdfr    return rx;
255178825Sdfr}
256178825Sdfr
257178825Sdfrstatic int
258178825Sdfrsec_send(int fd, char *from, int length)
259178825Sdfr{
260178825Sdfr    int bytes;
261178825Sdfr    void *buf;
262178825Sdfr    bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
263178825Sdfr    bytes = htonl(bytes);
264178825Sdfr    block_write(fd, &bytes, sizeof(bytes));
265178825Sdfr    block_write(fd, buf, ntohl(bytes));
266178825Sdfr    free(buf);
267178825Sdfr    return length;
268178825Sdfr}
269178825Sdfr
270178825Sdfrint
271178825Sdfrsec_fflush(FILE *F)
272178825Sdfr{
273178825Sdfr    if(data_prot != prot_clear) {
274178825Sdfr	if(out_buffer.index > 0){
275178825Sdfr	    sec_write(fileno(F), out_buffer.data, out_buffer.index);
276178825Sdfr	    out_buffer.index = 0;
277178825Sdfr	}
278178825Sdfr	sec_send(fileno(F), NULL, 0);
279178825Sdfr    }
280178825Sdfr    fflush(F);
281178825Sdfr    return 0;
282178825Sdfr}
283178825Sdfr
284178825Sdfrint
285178825Sdfrsec_write(int fd, char *dataptr, int length)
286178825Sdfr{
287178825Sdfr    int len = buffer_size;
288178825Sdfr    int tx = 0;
289178825Sdfr
290178825Sdfr    if(data_prot == prot_clear)
291178825Sdfr	return write(fd, dataptr, length);
292178825Sdfr
293178825Sdfr    len -= (*mech->overhead)(app_data, data_prot, len);
294178825Sdfr    while(length){
295178825Sdfr	if(length < len)
296178825Sdfr	    len = length;
297178825Sdfr	sec_send(fd, dataptr, len);
298178825Sdfr	length -= len;
299178825Sdfr	dataptr += len;
300178825Sdfr	tx += len;
301178825Sdfr    }
302178825Sdfr    return tx;
303178825Sdfr}
304178825Sdfr
305178825Sdfrint
306178825Sdfrsec_vfprintf2(FILE *f, const char *fmt, va_list ap)
307178825Sdfr{
308178825Sdfr    char *buf;
309178825Sdfr    int ret;
310178825Sdfr    if(data_prot == prot_clear)
311178825Sdfr	return vfprintf(f, fmt, ap);
312178825Sdfr    else {
313178825Sdfr	int len;
314178825Sdfr	len = vasprintf(&buf, fmt, ap);
315178825Sdfr	if (len == -1)
316178825Sdfr	    return len;
317178825Sdfr	ret = buffer_write(&out_buffer, buf, len);
318178825Sdfr	free(buf);
319178825Sdfr	return ret;
320178825Sdfr    }
321178825Sdfr}
322178825Sdfr
323178825Sdfrint
324178825Sdfrsec_fprintf2(FILE *f, const char *fmt, ...)
325178825Sdfr{
326178825Sdfr    int ret;
327178825Sdfr    va_list ap;
328178825Sdfr    va_start(ap, fmt);
329178825Sdfr    ret = sec_vfprintf2(f, fmt, ap);
330178825Sdfr    va_end(ap);
331178825Sdfr    return ret;
332178825Sdfr}
333178825Sdfr
334178825Sdfrint
335178825Sdfrsec_putc(int c, FILE *F)
336178825Sdfr{
337178825Sdfr    char ch = c;
338178825Sdfr    if(data_prot == prot_clear)
339178825Sdfr	return putc(c, F);
340178825Sdfr
341178825Sdfr    buffer_write(&out_buffer, &ch, 1);
342178825Sdfr    if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
343178825Sdfr	sec_write(fileno(F), out_buffer.data, out_buffer.index);
344178825Sdfr	out_buffer.index = 0;
345178825Sdfr    }
346178825Sdfr    return c;
347178825Sdfr}
348178825Sdfr
349178825Sdfrint
350178825Sdfrsec_read_msg(char *s, int level)
351178825Sdfr{
352178825Sdfr    int len;
353178825Sdfr    char *buf;
354178825Sdfr    int return_code;
355178825Sdfr
356178825Sdfr    buf = malloc(strlen(s));
357178825Sdfr    len = base64_decode(s + 4, buf); /* XXX */
358178825Sdfr
359178825Sdfr    len = (*mech->decode)(app_data, buf, len, level);
360178825Sdfr    if(len < 0)
361178825Sdfr	return -1;
362178825Sdfr
363178825Sdfr    buf[len] = '\0';
364178825Sdfr
365178825Sdfr    if(buf[3] == '-')
366178825Sdfr	return_code = 0;
367178825Sdfr    else
368178825Sdfr	sscanf(buf, "%d", &return_code);
369178825Sdfr    if(buf[len-1] == '\n')
370178825Sdfr	buf[len-1] = '\0';
371178825Sdfr    strcpy(s, buf);
372178825Sdfr    free(buf);
373178825Sdfr    return return_code;
374178825Sdfr}
375178825Sdfr
376178825Sdfrint
377178825Sdfrsec_vfprintf(FILE *f, const char *fmt, va_list ap)
378178825Sdfr{
379178825Sdfr    char *buf;
380178825Sdfr    void *enc;
381178825Sdfr    int len;
382178825Sdfr    if(!sec_complete)
383178825Sdfr	return vfprintf(f, fmt, ap);
384178825Sdfr
385178825Sdfr    if (vasprintf(&buf, fmt, ap) == -1) {
386178825Sdfr	printf("Failed to allocate command.\n");
387178825Sdfr	return -1;
388178825Sdfr    }
389178825Sdfr    len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
390178825Sdfr    free(buf);
391178825Sdfr    if(len < 0) {
392178825Sdfr	printf("Failed to encode command.\n");
393178825Sdfr	return -1;
394178825Sdfr    }
395178825Sdfr    if(base64_encode(enc, len, &buf) < 0){
396178825Sdfr	free(enc);
397178825Sdfr	printf("Out of memory base64-encoding.\n");
398178825Sdfr	return -1;
399178825Sdfr    }
400178825Sdfr    free(enc);
401178825Sdfr#ifdef FTP_SERVER
402178825Sdfr    if(command_prot == prot_safe)
403178825Sdfr	fprintf(f, "631 %s\r\n", buf);
404178825Sdfr    else if(command_prot == prot_private)
405178825Sdfr	fprintf(f, "632 %s\r\n", buf);
406178825Sdfr    else if(command_prot == prot_confidential)
407178825Sdfr	fprintf(f, "633 %s\r\n", buf);
408178825Sdfr#else
409178825Sdfr    if(command_prot == prot_safe)
410178825Sdfr	fprintf(f, "MIC %s", buf);
411178825Sdfr    else if(command_prot == prot_private)
412178825Sdfr	fprintf(f, "ENC %s", buf);
413178825Sdfr    else if(command_prot == prot_confidential)
414178825Sdfr	fprintf(f, "CONF %s", buf);
415178825Sdfr#endif
416178825Sdfr    free(buf);
417178825Sdfr    return 0;
418178825Sdfr}
419178825Sdfr
420178825Sdfrint
421178825Sdfrsec_fprintf(FILE *f, const char *fmt, ...)
422178825Sdfr{
423178825Sdfr    va_list ap;
424178825Sdfr    int ret;
425178825Sdfr    va_start(ap, fmt);
426178825Sdfr    ret = sec_vfprintf(f, fmt, ap);
427178825Sdfr    va_end(ap);
428178825Sdfr    return ret;
429178825Sdfr}
430178825Sdfr
431178825Sdfr/* end common stuff */
432178825Sdfr
433178825Sdfr#ifdef FTP_SERVER
434178825Sdfr
435178825Sdfrint ccc_passed;
436178825Sdfr
437178825Sdfrvoid
438178825Sdfrauth(char *auth_name)
439178825Sdfr{
440178825Sdfr    int i;
441178825Sdfr    void *tmp;
442178825Sdfr
443178825Sdfr    for(i = 0; (mech = mechs[i]) != NULL; i++){
444178825Sdfr	if(!strcasecmp(auth_name, mech->name)){
445178825Sdfr	    tmp = realloc(app_data, mech->size);
446178825Sdfr	    if (tmp == NULL) {
447178825Sdfr		reply(431, "Unable to accept %s at this time", mech->name);
448178825Sdfr		return;
449178825Sdfr	    }
450178825Sdfr	    app_data = tmp;
451178825Sdfr
452178825Sdfr	    if(mech->init && (*mech->init)(app_data) != 0) {
453178825Sdfr		reply(431, "Unable to accept %s at this time", mech->name);
454178825Sdfr		return;
455178825Sdfr	    }
456178825Sdfr	    if(mech->auth) {
457178825Sdfr		(*mech->auth)(app_data);
458178825Sdfr		return;
459178825Sdfr	    }
460178825Sdfr	    if(mech->adat)
461178825Sdfr		reply(334, "Send authorization data.");
462178825Sdfr	    else
463178825Sdfr		reply(234, "Authorization complete.");
464178825Sdfr	    return;
465178825Sdfr	}
466178825Sdfr    }
467178825Sdfr    free (app_data);
468178825Sdfr    app_data = NULL;
469178825Sdfr    reply(504, "%s is unknown to me", auth_name);
470178825Sdfr}
471178825Sdfr
472178825Sdfrvoid
473178825Sdfradat(char *auth_data)
474178825Sdfr{
475178825Sdfr    if(mech && !sec_complete) {
476178825Sdfr	void *buf = malloc(strlen(auth_data));
477178825Sdfr	size_t len;
478178825Sdfr	len = base64_decode(auth_data, buf);
479178825Sdfr	(*mech->adat)(app_data, buf, len);
480178825Sdfr	free(buf);
481178825Sdfr    } else
482178825Sdfr	reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
483178825Sdfr}
484178825Sdfr
485178825Sdfrvoid pbsz(int size)
486178825Sdfr{
487178825Sdfr    size_t new = size;
488178825Sdfr    if(!sec_complete)
489178825Sdfr	reply(503, "Incomplete security data exchange.");
490178825Sdfr    if(mech->pbsz)
491178825Sdfr	new = (*mech->pbsz)(app_data, size);
492178825Sdfr    if(buffer_size != new){
493178825Sdfr	buffer_size = size;
494178825Sdfr    }
495178825Sdfr    if(new != size)
496178825Sdfr	reply(200, "PBSZ=%lu", (unsigned long)new);
497178825Sdfr    else
498178825Sdfr	reply(200, "OK");
499178825Sdfr}
500178825Sdfr
501178825Sdfrvoid
502178825Sdfrprot(char *pl)
503178825Sdfr{
504178825Sdfr    int p = -1;
505178825Sdfr
506178825Sdfr    if(buffer_size == 0){
507178825Sdfr	reply(503, "No protection buffer size negotiated.");
508178825Sdfr	return;
509178825Sdfr    }
510178825Sdfr
511178825Sdfr    if(!strcasecmp(pl, "C"))
512178825Sdfr	p = prot_clear;
513178825Sdfr    else if(!strcasecmp(pl, "S"))
514178825Sdfr	p = prot_safe;
515178825Sdfr    else if(!strcasecmp(pl, "E"))
516178825Sdfr	p = prot_confidential;
517178825Sdfr    else if(!strcasecmp(pl, "P"))
518178825Sdfr	p = prot_private;
519178825Sdfr    else {
520178825Sdfr	reply(504, "Unrecognized protection level.");
521178825Sdfr	return;
522178825Sdfr    }
523178825Sdfr
524178825Sdfr    if(sec_complete){
525178825Sdfr	if((*mech->check_prot)(app_data, p)){
526178825Sdfr	    reply(536, "%s does not support %s protection.",
527178825Sdfr		  mech->name, level_to_name(p));
528178825Sdfr	}else{
529178825Sdfr	    data_prot = (enum protection_level)p;
530178825Sdfr	    reply(200, "Data protection is %s.", level_to_name(p));
531178825Sdfr	}
532178825Sdfr    }else{
533178825Sdfr	reply(503, "Incomplete security data exchange.");
534178825Sdfr    }
535178825Sdfr}
536178825Sdfr
537178825Sdfrvoid ccc(void)
538178825Sdfr{
539178825Sdfr    if(sec_complete){
540178825Sdfr	if(mech->ccc && (*mech->ccc)(app_data) == 0) {
541178825Sdfr	    command_prot = data_prot = prot_clear;
542178825Sdfr	    ccc_passed = 1;
543178825Sdfr	} else
544178825Sdfr	    reply(534, "You must be joking.");
545178825Sdfr    }else
546178825Sdfr	reply(503, "Incomplete security data exchange.");
547178825Sdfr}
548178825Sdfr
549178825Sdfrvoid mec(char *msg, enum protection_level level)
550178825Sdfr{
551178825Sdfr    void *buf;
552178825Sdfr    size_t len, buf_size;
553178825Sdfr    if(!sec_complete) {
554178825Sdfr	reply(503, "Incomplete security data exchange.");
555178825Sdfr	return;
556178825Sdfr    }
557178825Sdfr    buf_size = strlen(msg) + 2;
558178825Sdfr    buf = malloc(buf_size);
559178825Sdfr    len = base64_decode(msg, buf);
560178825Sdfr    command_prot = level;
561178825Sdfr    if(len == (size_t)-1) {
562178825Sdfr	reply(501, "Failed to base64-decode command");
563178825Sdfr	return;
564178825Sdfr    }
565178825Sdfr    len = (*mech->decode)(app_data, buf, len, level);
566178825Sdfr    if(len == (size_t)-1) {
567178825Sdfr	reply(535, "Failed to decode command");
568178825Sdfr	return;
569178825Sdfr    }
570178825Sdfr    ((char*)buf)[len] = '\0';
571178825Sdfr    if(strstr((char*)buf, "\r\n") == NULL)
572178825Sdfr	strlcat((char*)buf, "\r\n", buf_size);
573178825Sdfr    new_ftp_command(buf);
574178825Sdfr}
575178825Sdfr
576178825Sdfr/* ------------------------------------------------------------ */
577178825Sdfr
578178825Sdfrint
579178825Sdfrsec_userok(char *userstr)
580178825Sdfr{
581178825Sdfr    if(sec_complete)
582178825Sdfr	return (*mech->userok)(app_data, userstr);
583178825Sdfr    return 0;
584178825Sdfr}
585178825Sdfr
586178825Sdfrint
587178825Sdfrsec_session(char *user)
588178825Sdfr{
589178825Sdfr    if(sec_complete && mech->session)
590178825Sdfr	return (*mech->session)(app_data, user);
591178825Sdfr    return 0;
592178825Sdfr}
593178825Sdfr
594178825Sdfrchar *ftp_command;
595178825Sdfr
596178825Sdfrvoid
597178825Sdfrnew_ftp_command(char *command)
598178825Sdfr{
599178825Sdfr    ftp_command = command;
600178825Sdfr}
601178825Sdfr
602178825Sdfrvoid
603178825Sdfrdelete_ftp_command(void)
604178825Sdfr{
605178825Sdfr    free(ftp_command);
606178825Sdfr    ftp_command = NULL;
607178825Sdfr}
608178825Sdfr
609178825Sdfrint
610178825Sdfrsecure_command(void)
611178825Sdfr{
612178825Sdfr    return ftp_command != NULL;
613178825Sdfr}
614178825Sdfr
615178825Sdfrenum protection_level
616178825Sdfrget_command_prot(void)
617178825Sdfr{
618178825Sdfr    return command_prot;
619178825Sdfr}
620178825Sdfr
621178825Sdfr#else /* FTP_SERVER */
622178825Sdfr
623178825Sdfrvoid
624178825Sdfrsec_status(void)
625178825Sdfr{
626178825Sdfr    if(sec_complete){
627178825Sdfr	printf("Using %s for authentication.\n", mech->name);
628178825Sdfr	printf("Using %s command channel.\n", level_to_name(command_prot));
629178825Sdfr	printf("Using %s data channel.\n", level_to_name(data_prot));
630178825Sdfr	if(buffer_size > 0)
631178825Sdfr	    printf("Protection buffer size: %lu.\n",
632178825Sdfr		   (unsigned long)buffer_size);
633178825Sdfr    }else{
634178825Sdfr	printf("Not using any security mechanism.\n");
635178825Sdfr    }
636178825Sdfr}
637178825Sdfr
638178825Sdfrstatic int
639178825Sdfrsec_prot_internal(int level)
640178825Sdfr{
641178825Sdfr    int ret;
642178825Sdfr    char *p;
643178825Sdfr    unsigned int s = 1048576;
644178825Sdfr
645178825Sdfr    int old_verbose = verbose;
646178825Sdfr    verbose = 0;
647178825Sdfr
648178825Sdfr    if(!sec_complete){
649178825Sdfr	printf("No security data exchange has taken place.\n");
650178825Sdfr	return -1;
651178825Sdfr    }
652178825Sdfr
653178825Sdfr    if(level){
654178825Sdfr	ret = command("PBSZ %u", s);
655178825Sdfr	if(ret != COMPLETE){
656178825Sdfr	    printf("Failed to set protection buffer size.\n");
657178825Sdfr	    return -1;
658178825Sdfr	}
659178825Sdfr	buffer_size = s;
660178825Sdfr	p = strstr(reply_string, "PBSZ=");
661178825Sdfr	if(p)
662178825Sdfr	    sscanf(p, "PBSZ=%u", &s);
663178825Sdfr	if(s < buffer_size)
664178825Sdfr	    buffer_size = s;
665178825Sdfr    }
666178825Sdfr    verbose = old_verbose;
667178825Sdfr    ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
668178825Sdfr    if(ret != COMPLETE){
669178825Sdfr	printf("Failed to set protection level.\n");
670178825Sdfr	return -1;
671178825Sdfr    }
672178825Sdfr
673178825Sdfr    data_prot = (enum protection_level)level;
674178825Sdfr    return 0;
675178825Sdfr}
676178825Sdfr
677178825Sdfrenum protection_level
678178825Sdfrset_command_prot(enum protection_level level)
679178825Sdfr{
680178825Sdfr    int ret;
681178825Sdfr    enum protection_level old = command_prot;
682178825Sdfr    if(level != command_prot && level == prot_clear) {
683178825Sdfr	ret = command("CCC");
684178825Sdfr	if(ret != COMPLETE) {
685178825Sdfr	    printf("Failed to clear command channel.\n");
686178825Sdfr	    return -1;
687178825Sdfr	}
688178825Sdfr    }
689178825Sdfr    command_prot = level;
690178825Sdfr    return old;
691178825Sdfr}
692178825Sdfr
693178825Sdfrvoid
694178825Sdfrsec_prot(int argc, char **argv)
695178825Sdfr{
696178825Sdfr    int level = -1;
697178825Sdfr
698178825Sdfr    if(argc > 3)
699178825Sdfr	goto usage;
700178825Sdfr
701178825Sdfr    if(argc == 1) {
702178825Sdfr	sec_status();
703178825Sdfr	return;
704178825Sdfr    }
705178825Sdfr    if(!sec_complete) {
706178825Sdfr	printf("No security data exchange has taken place.\n");
707178825Sdfr	code = -1;
708178825Sdfr	return;
709178825Sdfr    }
710178825Sdfr    level = name_to_level(argv[argc - 1]);
711178825Sdfr
712178825Sdfr    if(level == -1)
713178825Sdfr	goto usage;
714178825Sdfr
715178825Sdfr    if((*mech->check_prot)(app_data, level)) {
716178825Sdfr	printf("%s does not implement %s protection.\n",
717178825Sdfr	       mech->name, level_to_name(level));
718178825Sdfr	code = -1;
719178825Sdfr	return;
720178825Sdfr    }
721178825Sdfr
722178825Sdfr    if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
723178825Sdfr	if(sec_prot_internal(level) < 0){
724178825Sdfr	    code = -1;
725178825Sdfr	    return;
726178825Sdfr	}
727178825Sdfr    } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) {
728178825Sdfr	if(set_command_prot(level) < 0) {
729178825Sdfr	    code = -1;
730178825Sdfr	    return;
731178825Sdfr	}
732178825Sdfr    } else
733178825Sdfr	goto usage;
734178825Sdfr    code = 0;
735178825Sdfr    return;
736178825Sdfr usage:
737178825Sdfr    printf("usage: %s [command|data] [clear|safe|confidential|private]\n",
738178825Sdfr	   argv[0]);
739178825Sdfr    code = -1;
740178825Sdfr}
741178825Sdfr
742178825Sdfrvoid
743178825Sdfrsec_prot_command(int argc, char **argv)
744178825Sdfr{
745178825Sdfr    int level;
746178825Sdfr
747178825Sdfr    if(argc > 2)
748178825Sdfr	goto usage;
749178825Sdfr
750178825Sdfr    if(!sec_complete) {
751178825Sdfr	printf("No security data exchange has taken place.\n");
752178825Sdfr	code = -1;
753178825Sdfr	return;
754178825Sdfr    }
755178825Sdfr
756178825Sdfr    if(argc == 1) {
757178825Sdfr	sec_status();
758178825Sdfr    } else {
759178825Sdfr	level = name_to_level(argv[1]);
760178825Sdfr	if(level == -1)
761178825Sdfr	    goto usage;
762178825Sdfr
763178825Sdfr	if((*mech->check_prot)(app_data, level)) {
764178825Sdfr	    printf("%s does not implement %s protection.\n",
765178825Sdfr		   mech->name, level_to_name(level));
766178825Sdfr	    code = -1;
767178825Sdfr	    return;
768178825Sdfr	}
769178825Sdfr	if(set_command_prot(level) < 0) {
770178825Sdfr	    code = -1;
771178825Sdfr	    return;
772178825Sdfr	}
773178825Sdfr    }
774178825Sdfr    code = 0;
775178825Sdfr    return;
776178825Sdfr usage:
777178825Sdfr    printf("usage: %s [clear|safe|confidential|private]\n",
778178825Sdfr	   argv[0]);
779178825Sdfr    code = -1;
780178825Sdfr}
781178825Sdfr
782178825Sdfrstatic enum protection_level request_data_prot;
783178825Sdfr
784178825Sdfrvoid
785178825Sdfrsec_set_protection_level(void)
786178825Sdfr{
787178825Sdfr    if(sec_complete && data_prot != request_data_prot)
788178825Sdfr	sec_prot_internal(request_data_prot);
789178825Sdfr}
790178825Sdfr
791178825Sdfr
792178825Sdfrint
793178825Sdfrsec_request_prot(char *level)
794178825Sdfr{
795178825Sdfr    int l = name_to_level(level);
796178825Sdfr    if(l == -1)
797178825Sdfr	return -1;
798178825Sdfr    request_data_prot = (enum protection_level)l;
799178825Sdfr    return 0;
800178825Sdfr}
801178825Sdfr
802178825Sdfrint
803178825Sdfrsec_login(char *host)
804178825Sdfr{
805178825Sdfr    int ret;
806178825Sdfr    struct sec_client_mech **m;
807178825Sdfr    int old_verbose = verbose;
808178825Sdfr
809178825Sdfr    verbose = -1; /* shut up all messages this will produce (they
810178825Sdfr		     are usually not very user friendly) */
811178825Sdfr
812178825Sdfr    for(m = mechs; *m && (*m)->name; m++) {
813178825Sdfr	void *tmp;
814178825Sdfr
815178825Sdfr	tmp = realloc(app_data, (*m)->size);
816178825Sdfr	if (tmp == NULL) {
817178825Sdfr	    warnx ("realloc %lu failed", (unsigned long)(*m)->size);
818178825Sdfr	    return -1;
819178825Sdfr	}
820178825Sdfr	app_data = tmp;
821178825Sdfr
822178825Sdfr	if((*m)->init && (*(*m)->init)(app_data) != 0) {
823178825Sdfr	    printf("Skipping %s...\n", (*m)->name);
824178825Sdfr	    continue;
825178825Sdfr	}
826178825Sdfr	printf("Trying %s...\n", (*m)->name);
827178825Sdfr	ret = command("AUTH %s", (*m)->name);
828178825Sdfr	if(ret != CONTINUE){
829178825Sdfr	    if(code == 504){
830178825Sdfr		printf("%s is not supported by the server.\n", (*m)->name);
831178825Sdfr	    }else if(code == 534){
832178825Sdfr		printf("%s rejected as security mechanism.\n", (*m)->name);
833178825Sdfr	    }else if(ret == ERROR) {
834178825Sdfr		printf("The server doesn't support the FTP "
835178825Sdfr		       "security extensions.\n");
836178825Sdfr		verbose = old_verbose;
837178825Sdfr		return -1;
838178825Sdfr	    }
839178825Sdfr	    continue;
840178825Sdfr	}
841178825Sdfr
842178825Sdfr	ret = (*(*m)->auth)(app_data, host);
843178825Sdfr
844178825Sdfr	if(ret == AUTH_CONTINUE)
845178825Sdfr	    continue;
846178825Sdfr	else if(ret != AUTH_OK){
847178825Sdfr	    /* mechanism is supposed to output error string */
848178825Sdfr	    verbose = old_verbose;
849178825Sdfr	    return -1;
850178825Sdfr	}
851178825Sdfr	mech = *m;
852178825Sdfr	sec_complete = 1;
853178825Sdfr	if(doencrypt) {
854178825Sdfr	    command_prot = prot_private;
855178825Sdfr	    request_data_prot = prot_private;
856178825Sdfr	} else {
857178825Sdfr	    command_prot = prot_safe;
858178825Sdfr	}
859178825Sdfr	break;
860178825Sdfr    }
861178825Sdfr
862178825Sdfr    verbose = old_verbose;
863178825Sdfr    return *m == NULL;
864178825Sdfr}
865178825Sdfr
866178825Sdfrvoid
867178825Sdfrsec_end(void)
868178825Sdfr{
869178825Sdfr    if (mech != NULL) {
870178825Sdfr	if(mech->end)
871178825Sdfr	    (*mech->end)(app_data);
872178825Sdfr	if (app_data != NULL) {
873178825Sdfr	    memset(app_data, 0, mech->size);
874178825Sdfr	    free(app_data);
875178825Sdfr	    app_data = NULL;
876178825Sdfr	}
877178825Sdfr    }
878178825Sdfr    sec_complete = 0;
879178825Sdfr    data_prot = (enum protection_level)0;
880178825Sdfr}
881178825Sdfr
882178825Sdfr#endif /* FTP_SERVER */
883178825Sdfr
884