1178825Sdfr/*
2233294Sstas * Copyright (c) 1998-2002, 2005 Kungliga Tekniska H��gskolan
3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * All rights reserved.
5233294Sstas *
6178825Sdfr * Redistribution and use in source and binary forms, with or without
7178825Sdfr * modification, are permitted provided that the following conditions
8178825Sdfr * are met:
9233294Sstas *
10178825Sdfr * 1. Redistributions of source code must retain the above copyright
11178825Sdfr *    notice, this list of conditions and the following disclaimer.
12233294Sstas *
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.
16233294Sstas *
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.
20233294Sstas *
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
40233294SstasRCSID("$Id$");
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 */
77233294Sstasstatic 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;
84233294Sstas    return prot_invalid;
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    NULL
95178825Sdfr};
96178825Sdfr
97178825Sdfrstatic struct sec_server_mech *mech;
98178825Sdfr
99178825Sdfr#else
100178825Sdfr
101178825Sdfrstatic struct sec_client_mech *mechs[] = {
102178825Sdfr#ifdef KRB5
103178825Sdfr    &gss_client_mech,
104178825Sdfr#endif
105178825Sdfr    NULL
106178825Sdfr};
107178825Sdfr
108178825Sdfrstatic struct sec_client_mech *mech;
109178825Sdfr
110178825Sdfr#endif
111178825Sdfr
112178825Sdfrstatic void *app_data;
113178825Sdfr
114178825Sdfrint
115178825Sdfrsec_getc(FILE *F)
116178825Sdfr{
117178825Sdfr    if(sec_complete && data_prot) {
118178825Sdfr	char c;
119178825Sdfr	if(sec_read(fileno(F), &c, 1) <= 0)
120178825Sdfr	    return EOF;
121178825Sdfr	return c;
122178825Sdfr    } else
123178825Sdfr	return getc(F);
124178825Sdfr}
125178825Sdfr
126178825Sdfrstatic int
127178825Sdfrblock_read(int fd, void *buf, size_t len)
128178825Sdfr{
129178825Sdfr    unsigned char *p = buf;
130178825Sdfr    int b;
131178825Sdfr    while(len) {
132178825Sdfr	b = read(fd, p, len);
133178825Sdfr	if (b == 0)
134178825Sdfr	    return 0;
135178825Sdfr	else if (b < 0)
136178825Sdfr	    return -1;
137178825Sdfr	len -= b;
138178825Sdfr	p += b;
139178825Sdfr    }
140178825Sdfr    return p - (unsigned char*)buf;
141178825Sdfr}
142178825Sdfr
143178825Sdfrstatic int
144178825Sdfrblock_write(int fd, void *buf, size_t len)
145178825Sdfr{
146178825Sdfr    unsigned char *p = buf;
147178825Sdfr    int b;
148178825Sdfr    while(len) {
149178825Sdfr	b = write(fd, p, len);
150178825Sdfr	if(b < 0)
151178825Sdfr	    return -1;
152178825Sdfr	len -= b;
153178825Sdfr	p += b;
154178825Sdfr    }
155178825Sdfr    return p - (unsigned char*)buf;
156178825Sdfr}
157178825Sdfr
158178825Sdfrstatic int
159178825Sdfrsec_get_data(int fd, struct buffer *buf, int level)
160178825Sdfr{
161178825Sdfr    int len;
162178825Sdfr    int b;
163178825Sdfr    void *tmp;
164178825Sdfr
165178825Sdfr    b = block_read(fd, &len, sizeof(len));
166178825Sdfr    if (b == 0)
167178825Sdfr	return 0;
168178825Sdfr    else if (b < 0)
169178825Sdfr	return -1;
170178825Sdfr    len = ntohl(len);
171178825Sdfr    tmp = realloc(buf->data, len);
172178825Sdfr    if (tmp == NULL)
173178825Sdfr	return -1;
174178825Sdfr    buf->data = tmp;
175178825Sdfr    b = block_read(fd, buf->data, len);
176178825Sdfr    if (b == 0)
177178825Sdfr	return 0;
178178825Sdfr    else if (b < 0)
179178825Sdfr	return -1;
180178825Sdfr    buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
181178825Sdfr    buf->index = 0;
182178825Sdfr    return 0;
183178825Sdfr}
184178825Sdfr
185178825Sdfrstatic size_t
186178825Sdfrbuffer_read(struct buffer *buf, void *dataptr, size_t len)
187178825Sdfr{
188178825Sdfr    len = min(len, buf->size - buf->index);
189178825Sdfr    memcpy(dataptr, (char*)buf->data + buf->index, len);
190178825Sdfr    buf->index += len;
191178825Sdfr    return len;
192178825Sdfr}
193178825Sdfr
194178825Sdfrstatic size_t
195178825Sdfrbuffer_write(struct buffer *buf, void *dataptr, size_t len)
196178825Sdfr{
197178825Sdfr    if(buf->index + len > buf->size) {
198178825Sdfr	void *tmp;
199178825Sdfr	if(buf->data == NULL)
200178825Sdfr	    tmp = malloc(1024);
201178825Sdfr	else
202178825Sdfr	    tmp = realloc(buf->data, buf->index + len);
203178825Sdfr	if(tmp == NULL)
204178825Sdfr	    return -1;
205178825Sdfr	buf->data = tmp;
206178825Sdfr	buf->size = buf->index + len;
207178825Sdfr    }
208178825Sdfr    memcpy((char*)buf->data + buf->index, dataptr, len);
209178825Sdfr    buf->index += len;
210178825Sdfr    return len;
211178825Sdfr}
212178825Sdfr
213178825Sdfrint
214178825Sdfrsec_read(int fd, void *dataptr, int length)
215178825Sdfr{
216178825Sdfr    size_t len;
217178825Sdfr    int rx = 0;
218178825Sdfr
219178825Sdfr    if(sec_complete == 0 || data_prot == 0)
220178825Sdfr	return read(fd, dataptr, length);
221178825Sdfr
222178825Sdfr    if(in_buffer.eof_flag){
223178825Sdfr	in_buffer.eof_flag = 0;
224178825Sdfr	return 0;
225178825Sdfr    }
226233294Sstas
227178825Sdfr    len = buffer_read(&in_buffer, dataptr, length);
228178825Sdfr    length -= len;
229178825Sdfr    rx += len;
230178825Sdfr    dataptr = (char*)dataptr + len;
231233294Sstas
232178825Sdfr    while(length){
233178825Sdfr	int ret;
234178825Sdfr
235178825Sdfr	ret = sec_get_data(fd, &in_buffer, data_prot);
236178825Sdfr	if (ret < 0)
237178825Sdfr	    return -1;
238178825Sdfr	if(ret == 0 && in_buffer.size == 0) {
239178825Sdfr	    if(rx)
240178825Sdfr		in_buffer.eof_flag = 1;
241178825Sdfr	    return rx;
242178825Sdfr	}
243178825Sdfr	len = buffer_read(&in_buffer, dataptr, length);
244178825Sdfr	length -= len;
245178825Sdfr	rx += len;
246178825Sdfr	dataptr = (char*)dataptr + len;
247178825Sdfr    }
248178825Sdfr    return rx;
249178825Sdfr}
250178825Sdfr
251178825Sdfrstatic int
252178825Sdfrsec_send(int fd, char *from, int length)
253178825Sdfr{
254178825Sdfr    int bytes;
255178825Sdfr    void *buf;
256178825Sdfr    bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
257178825Sdfr    bytes = htonl(bytes);
258178825Sdfr    block_write(fd, &bytes, sizeof(bytes));
259178825Sdfr    block_write(fd, buf, ntohl(bytes));
260178825Sdfr    free(buf);
261178825Sdfr    return length;
262178825Sdfr}
263178825Sdfr
264178825Sdfrint
265178825Sdfrsec_fflush(FILE *F)
266178825Sdfr{
267178825Sdfr    if(data_prot != prot_clear) {
268178825Sdfr	if(out_buffer.index > 0){
269178825Sdfr	    sec_write(fileno(F), out_buffer.data, out_buffer.index);
270178825Sdfr	    out_buffer.index = 0;
271178825Sdfr	}
272178825Sdfr	sec_send(fileno(F), NULL, 0);
273178825Sdfr    }
274178825Sdfr    fflush(F);
275178825Sdfr    return 0;
276178825Sdfr}
277178825Sdfr
278178825Sdfrint
279178825Sdfrsec_write(int fd, char *dataptr, int length)
280178825Sdfr{
281178825Sdfr    int len = buffer_size;
282178825Sdfr    int tx = 0;
283233294Sstas
284178825Sdfr    if(data_prot == prot_clear)
285178825Sdfr	return write(fd, dataptr, length);
286178825Sdfr
287178825Sdfr    len -= (*mech->overhead)(app_data, data_prot, len);
288178825Sdfr    while(length){
289178825Sdfr	if(length < len)
290178825Sdfr	    len = length;
291178825Sdfr	sec_send(fd, dataptr, len);
292178825Sdfr	length -= len;
293178825Sdfr	dataptr += len;
294178825Sdfr	tx += len;
295178825Sdfr    }
296178825Sdfr    return tx;
297178825Sdfr}
298178825Sdfr
299178825Sdfrint
300178825Sdfrsec_vfprintf2(FILE *f, const char *fmt, va_list ap)
301178825Sdfr{
302178825Sdfr    char *buf;
303178825Sdfr    int ret;
304178825Sdfr    if(data_prot == prot_clear)
305178825Sdfr	return vfprintf(f, fmt, ap);
306178825Sdfr    else {
307178825Sdfr	int len;
308178825Sdfr	len = vasprintf(&buf, fmt, ap);
309178825Sdfr	if (len == -1)
310178825Sdfr	    return len;
311178825Sdfr	ret = buffer_write(&out_buffer, buf, len);
312178825Sdfr	free(buf);
313178825Sdfr	return ret;
314178825Sdfr    }
315178825Sdfr}
316178825Sdfr
317178825Sdfrint
318178825Sdfrsec_fprintf2(FILE *f, const char *fmt, ...)
319178825Sdfr{
320178825Sdfr    int ret;
321178825Sdfr    va_list ap;
322178825Sdfr    va_start(ap, fmt);
323178825Sdfr    ret = sec_vfprintf2(f, fmt, ap);
324178825Sdfr    va_end(ap);
325178825Sdfr    return ret;
326178825Sdfr}
327178825Sdfr
328178825Sdfrint
329178825Sdfrsec_putc(int c, FILE *F)
330178825Sdfr{
331178825Sdfr    char ch = c;
332178825Sdfr    if(data_prot == prot_clear)
333178825Sdfr	return putc(c, F);
334233294Sstas
335178825Sdfr    buffer_write(&out_buffer, &ch, 1);
336178825Sdfr    if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
337178825Sdfr	sec_write(fileno(F), out_buffer.data, out_buffer.index);
338178825Sdfr	out_buffer.index = 0;
339178825Sdfr    }
340178825Sdfr    return c;
341178825Sdfr}
342178825Sdfr
343178825Sdfrint
344178825Sdfrsec_read_msg(char *s, int level)
345178825Sdfr{
346178825Sdfr    int len;
347178825Sdfr    char *buf;
348178825Sdfr    int return_code;
349233294Sstas
350178825Sdfr    buf = malloc(strlen(s));
351178825Sdfr    len = base64_decode(s + 4, buf); /* XXX */
352233294Sstas
353178825Sdfr    len = (*mech->decode)(app_data, buf, len, level);
354178825Sdfr    if(len < 0)
355178825Sdfr	return -1;
356233294Sstas
357178825Sdfr    buf[len] = '\0';
358178825Sdfr
359178825Sdfr    if(buf[3] == '-')
360178825Sdfr	return_code = 0;
361178825Sdfr    else
362178825Sdfr	sscanf(buf, "%d", &return_code);
363178825Sdfr    if(buf[len-1] == '\n')
364178825Sdfr	buf[len-1] = '\0';
365178825Sdfr    strcpy(s, buf);
366178825Sdfr    free(buf);
367178825Sdfr    return return_code;
368178825Sdfr}
369178825Sdfr
370178825Sdfrint
371178825Sdfrsec_vfprintf(FILE *f, const char *fmt, va_list ap)
372178825Sdfr{
373178825Sdfr    char *buf;
374178825Sdfr    void *enc;
375178825Sdfr    int len;
376178825Sdfr    if(!sec_complete)
377178825Sdfr	return vfprintf(f, fmt, ap);
378233294Sstas
379178825Sdfr    if (vasprintf(&buf, fmt, ap) == -1) {
380178825Sdfr	printf("Failed to allocate command.\n");
381178825Sdfr	return -1;
382178825Sdfr    }
383178825Sdfr    len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
384178825Sdfr    free(buf);
385178825Sdfr    if(len < 0) {
386178825Sdfr	printf("Failed to encode command.\n");
387178825Sdfr	return -1;
388178825Sdfr    }
389178825Sdfr    if(base64_encode(enc, len, &buf) < 0){
390178825Sdfr	free(enc);
391178825Sdfr	printf("Out of memory base64-encoding.\n");
392178825Sdfr	return -1;
393178825Sdfr    }
394178825Sdfr    free(enc);
395178825Sdfr#ifdef FTP_SERVER
396178825Sdfr    if(command_prot == prot_safe)
397178825Sdfr	fprintf(f, "631 %s\r\n", buf);
398178825Sdfr    else if(command_prot == prot_private)
399178825Sdfr	fprintf(f, "632 %s\r\n", buf);
400178825Sdfr    else if(command_prot == prot_confidential)
401178825Sdfr	fprintf(f, "633 %s\r\n", buf);
402178825Sdfr#else
403178825Sdfr    if(command_prot == prot_safe)
404178825Sdfr	fprintf(f, "MIC %s", buf);
405178825Sdfr    else if(command_prot == prot_private)
406178825Sdfr	fprintf(f, "ENC %s", buf);
407178825Sdfr    else if(command_prot == prot_confidential)
408178825Sdfr	fprintf(f, "CONF %s", buf);
409178825Sdfr#endif
410178825Sdfr    free(buf);
411178825Sdfr    return 0;
412178825Sdfr}
413178825Sdfr
414178825Sdfrint
415178825Sdfrsec_fprintf(FILE *f, const char *fmt, ...)
416178825Sdfr{
417178825Sdfr    va_list ap;
418178825Sdfr    int ret;
419178825Sdfr    va_start(ap, fmt);
420178825Sdfr    ret = sec_vfprintf(f, fmt, ap);
421178825Sdfr    va_end(ap);
422178825Sdfr    return ret;
423178825Sdfr}
424178825Sdfr
425178825Sdfr/* end common stuff */
426178825Sdfr
427178825Sdfr#ifdef FTP_SERVER
428178825Sdfr
429178825Sdfrint ccc_passed;
430178825Sdfr
431178825Sdfrvoid
432178825Sdfrauth(char *auth_name)
433178825Sdfr{
434178825Sdfr    int i;
435178825Sdfr    void *tmp;
436178825Sdfr
437178825Sdfr    for(i = 0; (mech = mechs[i]) != NULL; i++){
438178825Sdfr	if(!strcasecmp(auth_name, mech->name)){
439178825Sdfr	    tmp = realloc(app_data, mech->size);
440178825Sdfr	    if (tmp == NULL) {
441178825Sdfr		reply(431, "Unable to accept %s at this time", mech->name);
442178825Sdfr		return;
443178825Sdfr	    }
444178825Sdfr	    app_data = tmp;
445178825Sdfr
446178825Sdfr	    if(mech->init && (*mech->init)(app_data) != 0) {
447178825Sdfr		reply(431, "Unable to accept %s at this time", mech->name);
448178825Sdfr		return;
449178825Sdfr	    }
450178825Sdfr	    if(mech->auth) {
451178825Sdfr		(*mech->auth)(app_data);
452178825Sdfr		return;
453178825Sdfr	    }
454178825Sdfr	    if(mech->adat)
455178825Sdfr		reply(334, "Send authorization data.");
456178825Sdfr	    else
457178825Sdfr		reply(234, "Authorization complete.");
458178825Sdfr	    return;
459178825Sdfr	}
460178825Sdfr    }
461178825Sdfr    free (app_data);
462178825Sdfr    app_data = NULL;
463178825Sdfr    reply(504, "%s is unknown to me", auth_name);
464178825Sdfr}
465178825Sdfr
466178825Sdfrvoid
467178825Sdfradat(char *auth_data)
468178825Sdfr{
469178825Sdfr    if(mech && !sec_complete) {
470178825Sdfr	void *buf = malloc(strlen(auth_data));
471178825Sdfr	size_t len;
472178825Sdfr	len = base64_decode(auth_data, buf);
473178825Sdfr	(*mech->adat)(app_data, buf, len);
474178825Sdfr	free(buf);
475178825Sdfr    } else
476178825Sdfr	reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
477178825Sdfr}
478178825Sdfr
479178825Sdfrvoid pbsz(int size)
480178825Sdfr{
481178825Sdfr    size_t new = size;
482178825Sdfr    if(!sec_complete)
483178825Sdfr	reply(503, "Incomplete security data exchange.");
484178825Sdfr    if(mech->pbsz)
485178825Sdfr	new = (*mech->pbsz)(app_data, size);
486178825Sdfr    if(buffer_size != new){
487178825Sdfr	buffer_size = size;
488178825Sdfr    }
489178825Sdfr    if(new != size)
490178825Sdfr	reply(200, "PBSZ=%lu", (unsigned long)new);
491178825Sdfr    else
492178825Sdfr	reply(200, "OK");
493178825Sdfr}
494178825Sdfr
495178825Sdfrvoid
496178825Sdfrprot(char *pl)
497178825Sdfr{
498178825Sdfr    int p = -1;
499178825Sdfr
500178825Sdfr    if(buffer_size == 0){
501178825Sdfr	reply(503, "No protection buffer size negotiated.");
502178825Sdfr	return;
503178825Sdfr    }
504178825Sdfr
505178825Sdfr    if(!strcasecmp(pl, "C"))
506178825Sdfr	p = prot_clear;
507178825Sdfr    else if(!strcasecmp(pl, "S"))
508178825Sdfr	p = prot_safe;
509178825Sdfr    else if(!strcasecmp(pl, "E"))
510178825Sdfr	p = prot_confidential;
511178825Sdfr    else if(!strcasecmp(pl, "P"))
512178825Sdfr	p = prot_private;
513178825Sdfr    else {
514178825Sdfr	reply(504, "Unrecognized protection level.");
515178825Sdfr	return;
516178825Sdfr    }
517233294Sstas
518178825Sdfr    if(sec_complete){
519178825Sdfr	if((*mech->check_prot)(app_data, p)){
520233294Sstas	    reply(536, "%s does not support %s protection.",
521178825Sdfr		  mech->name, level_to_name(p));
522178825Sdfr	}else{
523178825Sdfr	    data_prot = (enum protection_level)p;
524178825Sdfr	    reply(200, "Data protection is %s.", level_to_name(p));
525178825Sdfr	}
526178825Sdfr    }else{
527178825Sdfr	reply(503, "Incomplete security data exchange.");
528178825Sdfr    }
529178825Sdfr}
530178825Sdfr
531178825Sdfrvoid ccc(void)
532178825Sdfr{
533178825Sdfr    if(sec_complete){
534178825Sdfr	if(mech->ccc && (*mech->ccc)(app_data) == 0) {
535178825Sdfr	    command_prot = data_prot = prot_clear;
536178825Sdfr	    ccc_passed = 1;
537178825Sdfr	} else
538178825Sdfr	    reply(534, "You must be joking.");
539178825Sdfr    }else
540178825Sdfr	reply(503, "Incomplete security data exchange.");
541178825Sdfr}
542178825Sdfr
543178825Sdfrvoid mec(char *msg, enum protection_level level)
544178825Sdfr{
545178825Sdfr    void *buf;
546178825Sdfr    size_t len, buf_size;
547178825Sdfr    if(!sec_complete) {
548178825Sdfr	reply(503, "Incomplete security data exchange.");
549178825Sdfr	return;
550178825Sdfr    }
551178825Sdfr    buf_size = strlen(msg) + 2;
552178825Sdfr    buf = malloc(buf_size);
553233294Sstas    if (buf == NULL) {
554233294Sstas	reply(501, "Failed to allocate %lu", (unsigned long)buf_size);
555233294Sstas	return;
556233294Sstas    }
557178825Sdfr    len = base64_decode(msg, buf);
558178825Sdfr    command_prot = level;
559178825Sdfr    if(len == (size_t)-1) {
560233294Sstas	free(buf);
561178825Sdfr	reply(501, "Failed to base64-decode command");
562178825Sdfr	return;
563178825Sdfr    }
564178825Sdfr    len = (*mech->decode)(app_data, buf, len, level);
565178825Sdfr    if(len == (size_t)-1) {
566233294Sstas	free(buf);
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)
631233294Sstas	    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    }
672233294Sstas
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");
686233294Sstas	    return prot_invalid;
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]);
711233294Sstas
712178825Sdfr    if(level == -1)
713178825Sdfr	goto usage;
714233294Sstas
715178825Sdfr    if((*mech->check_prot)(app_data, level)) {
716233294Sstas	printf("%s does not implement %s protection.\n",
717178825Sdfr	       mech->name, level_to_name(level));
718178825Sdfr	code = -1;
719178825Sdfr	return;
720178825Sdfr    }
721233294Sstas
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;
762233294Sstas
763178825Sdfr	if((*mech->check_prot)(app_data, level)) {
764233294Sstas	    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) */
811233294Sstas
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;
821233294Sstas
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);
843233294Sstas
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;
855233294Sstas	    request_data_prot = prot_private;
856178825Sdfr	} else {
857178825Sdfr	    command_prot = prot_safe;
858178825Sdfr	}
859178825Sdfr	break;
860178825Sdfr    }
861233294Sstas
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