155682Smarkm/*
2233294Sstas * Copyright (c) 1998-2002, 2005 Kungliga Tekniska H��gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
5233294Sstas *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
9233294Sstas *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
12233294Sstas *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
16233294Sstas *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
20233294Sstas *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#ifdef FTP_SERVER
3555682Smarkm#include "ftpd_locl.h"
3655682Smarkm#else
3755682Smarkm#include "ftp_locl.h"
3855682Smarkm#endif
3955682Smarkm
40233294SstasRCSID("$Id$");
4155682Smarkm
4255682Smarkmstatic enum protection_level command_prot;
4355682Smarkmstatic enum protection_level data_prot;
4455682Smarkmstatic size_t buffer_size;
4555682Smarkm
4655682Smarkmstruct buffer {
4755682Smarkm    void *data;
4855682Smarkm    size_t size;
4955682Smarkm    size_t index;
5055682Smarkm    int eof_flag;
5155682Smarkm};
5255682Smarkm
5355682Smarkmstatic struct buffer in_buffer, out_buffer;
5455682Smarkmint sec_complete;
5555682Smarkm
5655682Smarkmstatic struct {
5755682Smarkm    enum protection_level level;
5855682Smarkm    const char *name;
5955682Smarkm} level_names[] = {
6055682Smarkm    { prot_clear, "clear" },
6155682Smarkm    { prot_safe, "safe" },
6255682Smarkm    { prot_confidential, "confidential" },
6355682Smarkm    { prot_private, "private" }
6455682Smarkm};
6555682Smarkm
6655682Smarkmstatic const char *
6755682Smarkmlevel_to_name(enum protection_level level)
6855682Smarkm{
6955682Smarkm    int i;
7055682Smarkm    for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
7155682Smarkm	if(level_names[i].level == level)
7255682Smarkm	    return level_names[i].name;
7355682Smarkm    return "unknown";
7455682Smarkm}
7555682Smarkm
7655682Smarkm#ifndef FTP_SERVER /* not used in server */
77233294Sstasstatic enum protection_level
7855682Smarkmname_to_level(const char *name)
7955682Smarkm{
8055682Smarkm    int i;
8155682Smarkm    for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
8255682Smarkm	if(!strncasecmp(level_names[i].name, name, strlen(name)))
8355682Smarkm	    return level_names[i].level;
84233294Sstas    return prot_invalid;
8555682Smarkm}
8655682Smarkm#endif
8755682Smarkm
8855682Smarkm#ifdef FTP_SERVER
8955682Smarkm
9055682Smarkmstatic struct sec_server_mech *mechs[] = {
9155682Smarkm#ifdef KRB5
9255682Smarkm    &gss_server_mech,
9355682Smarkm#endif
9455682Smarkm    NULL
9555682Smarkm};
9655682Smarkm
9755682Smarkmstatic struct sec_server_mech *mech;
9855682Smarkm
9955682Smarkm#else
10055682Smarkm
10155682Smarkmstatic struct sec_client_mech *mechs[] = {
10255682Smarkm#ifdef KRB5
10355682Smarkm    &gss_client_mech,
10455682Smarkm#endif
10555682Smarkm    NULL
10655682Smarkm};
10755682Smarkm
10855682Smarkmstatic struct sec_client_mech *mech;
10955682Smarkm
11055682Smarkm#endif
11155682Smarkm
11255682Smarkmstatic void *app_data;
11355682Smarkm
11455682Smarkmint
11555682Smarkmsec_getc(FILE *F)
11655682Smarkm{
11755682Smarkm    if(sec_complete && data_prot) {
11855682Smarkm	char c;
11955682Smarkm	if(sec_read(fileno(F), &c, 1) <= 0)
12055682Smarkm	    return EOF;
12155682Smarkm	return c;
12255682Smarkm    } else
12355682Smarkm	return getc(F);
12455682Smarkm}
12555682Smarkm
12655682Smarkmstatic int
12755682Smarkmblock_read(int fd, void *buf, size_t len)
12855682Smarkm{
12955682Smarkm    unsigned char *p = buf;
13055682Smarkm    int b;
13155682Smarkm    while(len) {
13255682Smarkm	b = read(fd, p, len);
13355682Smarkm	if (b == 0)
13455682Smarkm	    return 0;
13555682Smarkm	else if (b < 0)
13655682Smarkm	    return -1;
13755682Smarkm	len -= b;
13855682Smarkm	p += b;
13955682Smarkm    }
14055682Smarkm    return p - (unsigned char*)buf;
14155682Smarkm}
14255682Smarkm
14355682Smarkmstatic int
14455682Smarkmblock_write(int fd, void *buf, size_t len)
14555682Smarkm{
14655682Smarkm    unsigned char *p = buf;
14755682Smarkm    int b;
14855682Smarkm    while(len) {
14955682Smarkm	b = write(fd, p, len);
15055682Smarkm	if(b < 0)
15155682Smarkm	    return -1;
15255682Smarkm	len -= b;
15355682Smarkm	p += b;
15455682Smarkm    }
15555682Smarkm    return p - (unsigned char*)buf;
15655682Smarkm}
15755682Smarkm
15855682Smarkmstatic int
15955682Smarkmsec_get_data(int fd, struct buffer *buf, int level)
16055682Smarkm{
16155682Smarkm    int len;
16255682Smarkm    int b;
16378527Sassar    void *tmp;
16455682Smarkm
16555682Smarkm    b = block_read(fd, &len, sizeof(len));
16655682Smarkm    if (b == 0)
16755682Smarkm	return 0;
16855682Smarkm    else if (b < 0)
16955682Smarkm	return -1;
17055682Smarkm    len = ntohl(len);
17178527Sassar    tmp = realloc(buf->data, len);
17278527Sassar    if (tmp == NULL)
17378527Sassar	return -1;
17478527Sassar    buf->data = tmp;
17555682Smarkm    b = block_read(fd, buf->data, len);
17655682Smarkm    if (b == 0)
17755682Smarkm	return 0;
17855682Smarkm    else if (b < 0)
17955682Smarkm	return -1;
18055682Smarkm    buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
18155682Smarkm    buf->index = 0;
18255682Smarkm    return 0;
18355682Smarkm}
18455682Smarkm
18555682Smarkmstatic size_t
186178825Sdfrbuffer_read(struct buffer *buf, void *dataptr, size_t len)
18755682Smarkm{
18855682Smarkm    len = min(len, buf->size - buf->index);
189178825Sdfr    memcpy(dataptr, (char*)buf->data + buf->index, len);
19055682Smarkm    buf->index += len;
19155682Smarkm    return len;
19255682Smarkm}
19355682Smarkm
19455682Smarkmstatic size_t
195178825Sdfrbuffer_write(struct buffer *buf, void *dataptr, size_t len)
19655682Smarkm{
19755682Smarkm    if(buf->index + len > buf->size) {
19855682Smarkm	void *tmp;
19955682Smarkm	if(buf->data == NULL)
20055682Smarkm	    tmp = malloc(1024);
20155682Smarkm	else
20255682Smarkm	    tmp = realloc(buf->data, buf->index + len);
20355682Smarkm	if(tmp == NULL)
20455682Smarkm	    return -1;
20555682Smarkm	buf->data = tmp;
20655682Smarkm	buf->size = buf->index + len;
20755682Smarkm    }
208178825Sdfr    memcpy((char*)buf->data + buf->index, dataptr, len);
20955682Smarkm    buf->index += len;
21055682Smarkm    return len;
21155682Smarkm}
21255682Smarkm
21355682Smarkmint
214178825Sdfrsec_read(int fd, void *dataptr, int length)
21555682Smarkm{
21655682Smarkm    size_t len;
21755682Smarkm    int rx = 0;
21855682Smarkm
21955682Smarkm    if(sec_complete == 0 || data_prot == 0)
220178825Sdfr	return read(fd, dataptr, length);
22155682Smarkm
22255682Smarkm    if(in_buffer.eof_flag){
22355682Smarkm	in_buffer.eof_flag = 0;
22455682Smarkm	return 0;
22555682Smarkm    }
226233294Sstas
227178825Sdfr    len = buffer_read(&in_buffer, dataptr, length);
22855682Smarkm    length -= len;
22955682Smarkm    rx += len;
230178825Sdfr    dataptr = (char*)dataptr + len;
231233294Sstas
23255682Smarkm    while(length){
23357416Smarkm	int ret;
23457416Smarkm
23557416Smarkm	ret = sec_get_data(fd, &in_buffer, data_prot);
23657416Smarkm	if (ret < 0)
23755682Smarkm	    return -1;
23872445Sassar	if(ret == 0 && in_buffer.size == 0) {
23955682Smarkm	    if(rx)
24055682Smarkm		in_buffer.eof_flag = 1;
24155682Smarkm	    return rx;
24255682Smarkm	}
243178825Sdfr	len = buffer_read(&in_buffer, dataptr, length);
24455682Smarkm	length -= len;
24555682Smarkm	rx += len;
246178825Sdfr	dataptr = (char*)dataptr + len;
24755682Smarkm    }
24855682Smarkm    return rx;
24955682Smarkm}
25055682Smarkm
25155682Smarkmstatic int
25255682Smarkmsec_send(int fd, char *from, int length)
25355682Smarkm{
25455682Smarkm    int bytes;
25555682Smarkm    void *buf;
25655682Smarkm    bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
25755682Smarkm    bytes = htonl(bytes);
25855682Smarkm    block_write(fd, &bytes, sizeof(bytes));
25955682Smarkm    block_write(fd, buf, ntohl(bytes));
26055682Smarkm    free(buf);
26155682Smarkm    return length;
26255682Smarkm}
26355682Smarkm
26455682Smarkmint
26555682Smarkmsec_fflush(FILE *F)
26655682Smarkm{
26755682Smarkm    if(data_prot != prot_clear) {
26855682Smarkm	if(out_buffer.index > 0){
26955682Smarkm	    sec_write(fileno(F), out_buffer.data, out_buffer.index);
27055682Smarkm	    out_buffer.index = 0;
27155682Smarkm	}
27255682Smarkm	sec_send(fileno(F), NULL, 0);
27355682Smarkm    }
27455682Smarkm    fflush(F);
27555682Smarkm    return 0;
27655682Smarkm}
27755682Smarkm
27855682Smarkmint
279178825Sdfrsec_write(int fd, char *dataptr, int length)
28055682Smarkm{
28155682Smarkm    int len = buffer_size;
28255682Smarkm    int tx = 0;
283233294Sstas
28455682Smarkm    if(data_prot == prot_clear)
285178825Sdfr	return write(fd, dataptr, length);
28655682Smarkm
28755682Smarkm    len -= (*mech->overhead)(app_data, data_prot, len);
28855682Smarkm    while(length){
28955682Smarkm	if(length < len)
29055682Smarkm	    len = length;
291178825Sdfr	sec_send(fd, dataptr, len);
29255682Smarkm	length -= len;
293178825Sdfr	dataptr += len;
29455682Smarkm	tx += len;
29555682Smarkm    }
29655682Smarkm    return tx;
29755682Smarkm}
29855682Smarkm
29955682Smarkmint
30055682Smarkmsec_vfprintf2(FILE *f, const char *fmt, va_list ap)
30155682Smarkm{
30255682Smarkm    char *buf;
30355682Smarkm    int ret;
30455682Smarkm    if(data_prot == prot_clear)
30555682Smarkm	return vfprintf(f, fmt, ap);
30655682Smarkm    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);
31255682Smarkm	free(buf);
31355682Smarkm	return ret;
31455682Smarkm    }
31555682Smarkm}
31655682Smarkm
31755682Smarkmint
31855682Smarkmsec_fprintf2(FILE *f, const char *fmt, ...)
31955682Smarkm{
32055682Smarkm    int ret;
32155682Smarkm    va_list ap;
32255682Smarkm    va_start(ap, fmt);
32355682Smarkm    ret = sec_vfprintf2(f, fmt, ap);
32455682Smarkm    va_end(ap);
32555682Smarkm    return ret;
32655682Smarkm}
32755682Smarkm
32855682Smarkmint
32955682Smarkmsec_putc(int c, FILE *F)
33055682Smarkm{
33155682Smarkm    char ch = c;
33255682Smarkm    if(data_prot == prot_clear)
33355682Smarkm	return putc(c, F);
334233294Sstas
33555682Smarkm    buffer_write(&out_buffer, &ch, 1);
33655682Smarkm    if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
33755682Smarkm	sec_write(fileno(F), out_buffer.data, out_buffer.index);
33855682Smarkm	out_buffer.index = 0;
33955682Smarkm    }
34055682Smarkm    return c;
34155682Smarkm}
34255682Smarkm
34355682Smarkmint
34455682Smarkmsec_read_msg(char *s, int level)
34555682Smarkm{
34655682Smarkm    int len;
34755682Smarkm    char *buf;
348178825Sdfr    int return_code;
349233294Sstas
35055682Smarkm    buf = malloc(strlen(s));
35155682Smarkm    len = base64_decode(s + 4, buf); /* XXX */
352233294Sstas
35355682Smarkm    len = (*mech->decode)(app_data, buf, len, level);
35455682Smarkm    if(len < 0)
35555682Smarkm	return -1;
356233294Sstas
35755682Smarkm    buf[len] = '\0';
35855682Smarkm
35955682Smarkm    if(buf[3] == '-')
360178825Sdfr	return_code = 0;
36155682Smarkm    else
362178825Sdfr	sscanf(buf, "%d", &return_code);
36355682Smarkm    if(buf[len-1] == '\n')
36455682Smarkm	buf[len-1] = '\0';
36555682Smarkm    strcpy(s, buf);
36655682Smarkm    free(buf);
367178825Sdfr    return return_code;
36855682Smarkm}
36955682Smarkm
37055682Smarkmint
37155682Smarkmsec_vfprintf(FILE *f, const char *fmt, va_list ap)
37255682Smarkm{
37355682Smarkm    char *buf;
37455682Smarkm    void *enc;
37555682Smarkm    int len;
37655682Smarkm    if(!sec_complete)
37755682Smarkm	return vfprintf(f, fmt, ap);
378233294Sstas
379178825Sdfr    if (vasprintf(&buf, fmt, ap) == -1) {
380178825Sdfr	printf("Failed to allocate command.\n");
381178825Sdfr	return -1;
382178825Sdfr    }
38355682Smarkm    len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
38455682Smarkm    free(buf);
38555682Smarkm    if(len < 0) {
38655682Smarkm	printf("Failed to encode command.\n");
38755682Smarkm	return -1;
38855682Smarkm    }
38955682Smarkm    if(base64_encode(enc, len, &buf) < 0){
390103423Snectar	free(enc);
39155682Smarkm	printf("Out of memory base64-encoding.\n");
39255682Smarkm	return -1;
39355682Smarkm    }
394103423Snectar    free(enc);
39555682Smarkm#ifdef FTP_SERVER
39655682Smarkm    if(command_prot == prot_safe)
39755682Smarkm	fprintf(f, "631 %s\r\n", buf);
39855682Smarkm    else if(command_prot == prot_private)
39955682Smarkm	fprintf(f, "632 %s\r\n", buf);
40055682Smarkm    else if(command_prot == prot_confidential)
40155682Smarkm	fprintf(f, "633 %s\r\n", buf);
40255682Smarkm#else
40355682Smarkm    if(command_prot == prot_safe)
40455682Smarkm	fprintf(f, "MIC %s", buf);
40555682Smarkm    else if(command_prot == prot_private)
40655682Smarkm	fprintf(f, "ENC %s", buf);
40755682Smarkm    else if(command_prot == prot_confidential)
40855682Smarkm	fprintf(f, "CONF %s", buf);
40955682Smarkm#endif
41055682Smarkm    free(buf);
41155682Smarkm    return 0;
41255682Smarkm}
41355682Smarkm
41455682Smarkmint
41555682Smarkmsec_fprintf(FILE *f, const char *fmt, ...)
41655682Smarkm{
41755682Smarkm    va_list ap;
41855682Smarkm    int ret;
41955682Smarkm    va_start(ap, fmt);
42055682Smarkm    ret = sec_vfprintf(f, fmt, ap);
42155682Smarkm    va_end(ap);
42255682Smarkm    return ret;
42355682Smarkm}
42455682Smarkm
42555682Smarkm/* end common stuff */
42655682Smarkm
42755682Smarkm#ifdef FTP_SERVER
42855682Smarkm
429178825Sdfrint ccc_passed;
430178825Sdfr
43155682Smarkmvoid
43255682Smarkmauth(char *auth_name)
43355682Smarkm{
43455682Smarkm    int i;
43578527Sassar    void *tmp;
43678527Sassar
43755682Smarkm    for(i = 0; (mech = mechs[i]) != NULL; i++){
43855682Smarkm	if(!strcasecmp(auth_name, mech->name)){
43978527Sassar	    tmp = realloc(app_data, mech->size);
44078527Sassar	    if (tmp == NULL) {
44178527Sassar		reply(431, "Unable to accept %s at this time", mech->name);
44278527Sassar		return;
44378527Sassar	    }
44478527Sassar	    app_data = tmp;
44578527Sassar
44655682Smarkm	    if(mech->init && (*mech->init)(app_data) != 0) {
44755682Smarkm		reply(431, "Unable to accept %s at this time", mech->name);
44855682Smarkm		return;
44955682Smarkm	    }
45055682Smarkm	    if(mech->auth) {
45155682Smarkm		(*mech->auth)(app_data);
45255682Smarkm		return;
45355682Smarkm	    }
45455682Smarkm	    if(mech->adat)
45555682Smarkm		reply(334, "Send authorization data.");
45655682Smarkm	    else
45755682Smarkm		reply(234, "Authorization complete.");
45855682Smarkm	    return;
45955682Smarkm	}
46055682Smarkm    }
46155682Smarkm    free (app_data);
46278527Sassar    app_data = NULL;
46355682Smarkm    reply(504, "%s is unknown to me", auth_name);
46455682Smarkm}
46555682Smarkm
46655682Smarkmvoid
46755682Smarkmadat(char *auth_data)
46855682Smarkm{
46955682Smarkm    if(mech && !sec_complete) {
47055682Smarkm	void *buf = malloc(strlen(auth_data));
47155682Smarkm	size_t len;
47255682Smarkm	len = base64_decode(auth_data, buf);
47355682Smarkm	(*mech->adat)(app_data, buf, len);
47455682Smarkm	free(buf);
47555682Smarkm    } else
47655682Smarkm	reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
47755682Smarkm}
47855682Smarkm
47955682Smarkmvoid pbsz(int size)
48055682Smarkm{
48155682Smarkm    size_t new = size;
48255682Smarkm    if(!sec_complete)
48355682Smarkm	reply(503, "Incomplete security data exchange.");
48455682Smarkm    if(mech->pbsz)
48555682Smarkm	new = (*mech->pbsz)(app_data, size);
48655682Smarkm    if(buffer_size != new){
48755682Smarkm	buffer_size = size;
48855682Smarkm    }
48955682Smarkm    if(new != size)
49055682Smarkm	reply(200, "PBSZ=%lu", (unsigned long)new);
49155682Smarkm    else
49255682Smarkm	reply(200, "OK");
49355682Smarkm}
49455682Smarkm
49555682Smarkmvoid
49655682Smarkmprot(char *pl)
49755682Smarkm{
49855682Smarkm    int p = -1;
49955682Smarkm
50055682Smarkm    if(buffer_size == 0){
50155682Smarkm	reply(503, "No protection buffer size negotiated.");
50255682Smarkm	return;
50355682Smarkm    }
50455682Smarkm
50555682Smarkm    if(!strcasecmp(pl, "C"))
50655682Smarkm	p = prot_clear;
50755682Smarkm    else if(!strcasecmp(pl, "S"))
50855682Smarkm	p = prot_safe;
50955682Smarkm    else if(!strcasecmp(pl, "E"))
51055682Smarkm	p = prot_confidential;
51155682Smarkm    else if(!strcasecmp(pl, "P"))
51255682Smarkm	p = prot_private;
51355682Smarkm    else {
51455682Smarkm	reply(504, "Unrecognized protection level.");
51555682Smarkm	return;
51655682Smarkm    }
517233294Sstas
51855682Smarkm    if(sec_complete){
51955682Smarkm	if((*mech->check_prot)(app_data, p)){
520233294Sstas	    reply(536, "%s does not support %s protection.",
52155682Smarkm		  mech->name, level_to_name(p));
52255682Smarkm	}else{
52355682Smarkm	    data_prot = (enum protection_level)p;
52455682Smarkm	    reply(200, "Data protection is %s.", level_to_name(p));
52555682Smarkm	}
52655682Smarkm    }else{
52755682Smarkm	reply(503, "Incomplete security data exchange.");
52855682Smarkm    }
52955682Smarkm}
53055682Smarkm
53155682Smarkmvoid ccc(void)
53255682Smarkm{
53355682Smarkm    if(sec_complete){
534178825Sdfr	if(mech->ccc && (*mech->ccc)(app_data) == 0) {
53555682Smarkm	    command_prot = data_prot = prot_clear;
536178825Sdfr	    ccc_passed = 1;
537178825Sdfr	} else
53855682Smarkm	    reply(534, "You must be joking.");
53955682Smarkm    }else
54055682Smarkm	reply(503, "Incomplete security data exchange.");
54155682Smarkm}
54255682Smarkm
54355682Smarkmvoid mec(char *msg, enum protection_level level)
54455682Smarkm{
54555682Smarkm    void *buf;
546178825Sdfr    size_t len, buf_size;
54755682Smarkm    if(!sec_complete) {
54855682Smarkm	reply(503, "Incomplete security data exchange.");
54955682Smarkm	return;
55055682Smarkm    }
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    }
55755682Smarkm    len = base64_decode(msg, buf);
55855682Smarkm    command_prot = level;
55955682Smarkm    if(len == (size_t)-1) {
560233294Sstas	free(buf);
56155682Smarkm	reply(501, "Failed to base64-decode command");
56255682Smarkm	return;
56355682Smarkm    }
56455682Smarkm    len = (*mech->decode)(app_data, buf, len, level);
56555682Smarkm    if(len == (size_t)-1) {
566233294Sstas	free(buf);
56755682Smarkm	reply(535, "Failed to decode command");
56855682Smarkm	return;
56955682Smarkm    }
57055682Smarkm    ((char*)buf)[len] = '\0';
57155682Smarkm    if(strstr((char*)buf, "\r\n") == NULL)
572178825Sdfr	strlcat((char*)buf, "\r\n", buf_size);
57355682Smarkm    new_ftp_command(buf);
57455682Smarkm}
57555682Smarkm
57655682Smarkm/* ------------------------------------------------------------ */
57755682Smarkm
57855682Smarkmint
579178825Sdfrsec_userok(char *userstr)
58055682Smarkm{
58155682Smarkm    if(sec_complete)
582178825Sdfr	return (*mech->userok)(app_data, userstr);
58355682Smarkm    return 0;
58455682Smarkm}
58555682Smarkm
586178825Sdfrint
587178825Sdfrsec_session(char *user)
588178825Sdfr{
589178825Sdfr    if(sec_complete && mech->session)
590178825Sdfr	return (*mech->session)(app_data, user);
591178825Sdfr    return 0;
592178825Sdfr}
593178825Sdfr
59455682Smarkmchar *ftp_command;
59555682Smarkm
59655682Smarkmvoid
59755682Smarkmnew_ftp_command(char *command)
59855682Smarkm{
59955682Smarkm    ftp_command = command;
60055682Smarkm}
60155682Smarkm
60255682Smarkmvoid
60355682Smarkmdelete_ftp_command(void)
60455682Smarkm{
60555682Smarkm    free(ftp_command);
60655682Smarkm    ftp_command = NULL;
60755682Smarkm}
60855682Smarkm
60955682Smarkmint
61055682Smarkmsecure_command(void)
61155682Smarkm{
61255682Smarkm    return ftp_command != NULL;
61355682Smarkm}
61455682Smarkm
61555682Smarkmenum protection_level
61655682Smarkmget_command_prot(void)
61755682Smarkm{
61855682Smarkm    return command_prot;
61955682Smarkm}
62055682Smarkm
62155682Smarkm#else /* FTP_SERVER */
62255682Smarkm
62355682Smarkmvoid
62455682Smarkmsec_status(void)
62555682Smarkm{
62655682Smarkm    if(sec_complete){
62755682Smarkm	printf("Using %s for authentication.\n", mech->name);
62855682Smarkm	printf("Using %s command channel.\n", level_to_name(command_prot));
62955682Smarkm	printf("Using %s data channel.\n", level_to_name(data_prot));
63055682Smarkm	if(buffer_size > 0)
631233294Sstas	    printf("Protection buffer size: %lu.\n",
63255682Smarkm		   (unsigned long)buffer_size);
63355682Smarkm    }else{
63455682Smarkm	printf("Not using any security mechanism.\n");
63555682Smarkm    }
63655682Smarkm}
63755682Smarkm
63855682Smarkmstatic int
63955682Smarkmsec_prot_internal(int level)
64055682Smarkm{
64155682Smarkm    int ret;
64255682Smarkm    char *p;
64355682Smarkm    unsigned int s = 1048576;
64455682Smarkm
64555682Smarkm    int old_verbose = verbose;
64655682Smarkm    verbose = 0;
64755682Smarkm
64855682Smarkm    if(!sec_complete){
64955682Smarkm	printf("No security data exchange has taken place.\n");
65055682Smarkm	return -1;
65155682Smarkm    }
65255682Smarkm
65355682Smarkm    if(level){
65455682Smarkm	ret = command("PBSZ %u", s);
65555682Smarkm	if(ret != COMPLETE){
65655682Smarkm	    printf("Failed to set protection buffer size.\n");
65755682Smarkm	    return -1;
65855682Smarkm	}
65955682Smarkm	buffer_size = s;
66055682Smarkm	p = strstr(reply_string, "PBSZ=");
66155682Smarkm	if(p)
66255682Smarkm	    sscanf(p, "PBSZ=%u", &s);
66355682Smarkm	if(s < buffer_size)
66455682Smarkm	    buffer_size = s;
66555682Smarkm    }
66655682Smarkm    verbose = old_verbose;
66755682Smarkm    ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
66855682Smarkm    if(ret != COMPLETE){
66955682Smarkm	printf("Failed to set protection level.\n");
67055682Smarkm	return -1;
67155682Smarkm    }
672233294Sstas
67355682Smarkm    data_prot = (enum protection_level)level;
67455682Smarkm    return 0;
67555682Smarkm}
67655682Smarkm
67755682Smarkmenum protection_level
67855682Smarkmset_command_prot(enum protection_level level)
67955682Smarkm{
680178825Sdfr    int ret;
68155682Smarkm    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    }
68955682Smarkm    command_prot = level;
69055682Smarkm    return old;
69155682Smarkm}
69255682Smarkm
69355682Smarkmvoid
69455682Smarkmsec_prot(int argc, char **argv)
69555682Smarkm{
69655682Smarkm    int level = -1;
69755682Smarkm
698178825Sdfr    if(argc > 3)
69955682Smarkm	goto usage;
700178825Sdfr
701178825Sdfr    if(argc == 1) {
702178825Sdfr	sec_status();
703178825Sdfr	return;
704178825Sdfr    }
70555682Smarkm    if(!sec_complete) {
70655682Smarkm	printf("No security data exchange has taken place.\n");
70755682Smarkm	code = -1;
70855682Smarkm	return;
70955682Smarkm    }
71055682Smarkm    level = name_to_level(argv[argc - 1]);
711233294Sstas
71255682Smarkm    if(level == -1)
71355682Smarkm	goto usage;
714233294Sstas
71555682Smarkm    if((*mech->check_prot)(app_data, level)) {
716233294Sstas	printf("%s does not implement %s protection.\n",
71755682Smarkm	       mech->name, level_to_name(level));
71855682Smarkm	code = -1;
71955682Smarkm	return;
72055682Smarkm    }
721233294Sstas
72255682Smarkm    if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
72355682Smarkm	if(sec_prot_internal(level) < 0){
72455682Smarkm	    code = -1;
72555682Smarkm	    return;
72655682Smarkm	}
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
73355682Smarkm	goto usage;
73455682Smarkm    code = 0;
73555682Smarkm    return;
73655682Smarkm usage:
73755682Smarkm    printf("usage: %s [command|data] [clear|safe|confidential|private]\n",
73855682Smarkm	   argv[0]);
73955682Smarkm    code = -1;
74055682Smarkm}
74155682Smarkm
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
78255682Smarkmstatic enum protection_level request_data_prot;
78355682Smarkm
78455682Smarkmvoid
78555682Smarkmsec_set_protection_level(void)
78655682Smarkm{
78755682Smarkm    if(sec_complete && data_prot != request_data_prot)
78855682Smarkm	sec_prot_internal(request_data_prot);
78955682Smarkm}
79055682Smarkm
79155682Smarkm
79255682Smarkmint
79355682Smarkmsec_request_prot(char *level)
79455682Smarkm{
79555682Smarkm    int l = name_to_level(level);
79655682Smarkm    if(l == -1)
79755682Smarkm	return -1;
79855682Smarkm    request_data_prot = (enum protection_level)l;
79955682Smarkm    return 0;
80055682Smarkm}
80155682Smarkm
80255682Smarkmint
80355682Smarkmsec_login(char *host)
80455682Smarkm{
80555682Smarkm    int ret;
80655682Smarkm    struct sec_client_mech **m;
80755682Smarkm    int old_verbose = verbose;
80855682Smarkm
80955682Smarkm    verbose = -1; /* shut up all messages this will produce (they
81055682Smarkm		     are usually not very user friendly) */
811233294Sstas
81255682Smarkm    for(m = mechs; *m && (*m)->name; m++) {
81355682Smarkm	void *tmp;
81455682Smarkm
81555682Smarkm	tmp = realloc(app_data, (*m)->size);
81655682Smarkm	if (tmp == NULL) {
817178825Sdfr	    warnx ("realloc %lu failed", (unsigned long)(*m)->size);
81855682Smarkm	    return -1;
81955682Smarkm	}
82055682Smarkm	app_data = tmp;
821233294Sstas
82255682Smarkm	if((*m)->init && (*(*m)->init)(app_data) != 0) {
82355682Smarkm	    printf("Skipping %s...\n", (*m)->name);
82455682Smarkm	    continue;
82555682Smarkm	}
82655682Smarkm	printf("Trying %s...\n", (*m)->name);
82755682Smarkm	ret = command("AUTH %s", (*m)->name);
82855682Smarkm	if(ret != CONTINUE){
82955682Smarkm	    if(code == 504){
83055682Smarkm		printf("%s is not supported by the server.\n", (*m)->name);
83155682Smarkm	    }else if(code == 534){
83255682Smarkm		printf("%s rejected as security mechanism.\n", (*m)->name);
83355682Smarkm	    }else if(ret == ERROR) {
83455682Smarkm		printf("The server doesn't support the FTP "
83555682Smarkm		       "security extensions.\n");
83655682Smarkm		verbose = old_verbose;
83755682Smarkm		return -1;
83855682Smarkm	    }
83955682Smarkm	    continue;
84055682Smarkm	}
84155682Smarkm
84255682Smarkm	ret = (*(*m)->auth)(app_data, host);
843233294Sstas
84455682Smarkm	if(ret == AUTH_CONTINUE)
84555682Smarkm	    continue;
84655682Smarkm	else if(ret != AUTH_OK){
84755682Smarkm	    /* mechanism is supposed to output error string */
84855682Smarkm	    verbose = old_verbose;
84955682Smarkm	    return -1;
85055682Smarkm	}
85155682Smarkm	mech = *m;
85255682Smarkm	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	}
85955682Smarkm	break;
86055682Smarkm    }
861233294Sstas
86255682Smarkm    verbose = old_verbose;
86355682Smarkm    return *m == NULL;
86455682Smarkm}
86555682Smarkm
86655682Smarkmvoid
86755682Smarkmsec_end(void)
86855682Smarkm{
86955682Smarkm    if (mech != NULL) {
87055682Smarkm	if(mech->end)
87155682Smarkm	    (*mech->end)(app_data);
87278527Sassar	if (app_data != NULL) {
87378527Sassar	    memset(app_data, 0, mech->size);
87478527Sassar	    free(app_data);
87578527Sassar	    app_data = NULL;
87678527Sassar	}
87755682Smarkm    }
87855682Smarkm    sec_complete = 0;
87955682Smarkm    data_prot = (enum protection_level)0;
88055682Smarkm}
88155682Smarkm
88255682Smarkm#endif /* FTP_SERVER */
88355682Smarkm
884