security.c revision 72445
1/*
2 * Copyright (c) 1998-2000 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#ifdef FTP_SERVER
35#include "ftpd_locl.h"
36#else
37#include "ftp_locl.h"
38#endif
39
40RCSID("$Id: security.c,v 1.17 2000/11/08 23:30:32 joda Exp $");
41
42static enum protection_level command_prot;
43static enum protection_level data_prot;
44static size_t buffer_size;
45
46struct buffer {
47    void *data;
48    size_t size;
49    size_t index;
50    int eof_flag;
51};
52
53static struct buffer in_buffer, out_buffer;
54int sec_complete;
55
56static struct {
57    enum protection_level level;
58    const char *name;
59} level_names[] = {
60    { prot_clear, "clear" },
61    { prot_safe, "safe" },
62    { prot_confidential, "confidential" },
63    { prot_private, "private" }
64};
65
66static const char *
67level_to_name(enum protection_level level)
68{
69    int i;
70    for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
71	if(level_names[i].level == level)
72	    return level_names[i].name;
73    return "unknown";
74}
75
76#ifndef FTP_SERVER /* not used in server */
77static enum protection_level
78name_to_level(const char *name)
79{
80    int i;
81    for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
82	if(!strncasecmp(level_names[i].name, name, strlen(name)))
83	    return level_names[i].level;
84    return (enum protection_level)-1;
85}
86#endif
87
88#ifdef FTP_SERVER
89
90static struct sec_server_mech *mechs[] = {
91#ifdef KRB5
92    &gss_server_mech,
93#endif
94#ifdef KRB4
95    &krb4_server_mech,
96#endif
97    NULL
98};
99
100static struct sec_server_mech *mech;
101
102#else
103
104static struct sec_client_mech *mechs[] = {
105#ifdef KRB5
106    &gss_client_mech,
107#endif
108#ifdef KRB4
109    &krb4_client_mech,
110#endif
111    NULL
112};
113
114static struct sec_client_mech *mech;
115
116#endif
117
118static void *app_data;
119
120int
121sec_getc(FILE *F)
122{
123    if(sec_complete && data_prot) {
124	char c;
125	if(sec_read(fileno(F), &c, 1) <= 0)
126	    return EOF;
127	return c;
128    } else
129	return getc(F);
130}
131
132static int
133block_read(int fd, void *buf, size_t len)
134{
135    unsigned char *p = buf;
136    int b;
137    while(len) {
138	b = read(fd, p, len);
139	if (b == 0)
140	    return 0;
141	else if (b < 0)
142	    return -1;
143	len -= b;
144	p += b;
145    }
146    return p - (unsigned char*)buf;
147}
148
149static int
150block_write(int fd, void *buf, size_t len)
151{
152    unsigned char *p = buf;
153    int b;
154    while(len) {
155	b = write(fd, p, len);
156	if(b < 0)
157	    return -1;
158	len -= b;
159	p += b;
160    }
161    return p - (unsigned char*)buf;
162}
163
164static int
165sec_get_data(int fd, struct buffer *buf, int level)
166{
167    int len;
168    int b;
169
170    b = block_read(fd, &len, sizeof(len));
171    if (b == 0)
172	return 0;
173    else if (b < 0)
174	return -1;
175    len = ntohl(len);
176    buf->data = realloc(buf->data, len);
177    b = block_read(fd, buf->data, len);
178    if (b == 0)
179	return 0;
180    else if (b < 0)
181	return -1;
182    buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
183    buf->index = 0;
184    return 0;
185}
186
187static size_t
188buffer_read(struct buffer *buf, void *data, size_t len)
189{
190    len = min(len, buf->size - buf->index);
191    memcpy(data, (char*)buf->data + buf->index, len);
192    buf->index += len;
193    return len;
194}
195
196static size_t
197buffer_write(struct buffer *buf, void *data, size_t len)
198{
199    if(buf->index + len > buf->size) {
200	void *tmp;
201	if(buf->data == NULL)
202	    tmp = malloc(1024);
203	else
204	    tmp = realloc(buf->data, buf->index + len);
205	if(tmp == NULL)
206	    return -1;
207	buf->data = tmp;
208	buf->size = buf->index + len;
209    }
210    memcpy((char*)buf->data + buf->index, data, len);
211    buf->index += len;
212    return len;
213}
214
215int
216sec_read(int fd, void *data, int length)
217{
218    size_t len;
219    int rx = 0;
220
221    if(sec_complete == 0 || data_prot == 0)
222	return read(fd, data, length);
223
224    if(in_buffer.eof_flag){
225	in_buffer.eof_flag = 0;
226	return 0;
227    }
228
229    len = buffer_read(&in_buffer, data, length);
230    length -= len;
231    rx += len;
232    data = (char*)data + len;
233
234    while(length){
235	int ret;
236
237	ret = sec_get_data(fd, &in_buffer, data_prot);
238	if (ret < 0)
239	    return -1;
240	if(ret == 0 && in_buffer.size == 0) {
241	    if(rx)
242		in_buffer.eof_flag = 1;
243	    return rx;
244	}
245	len = buffer_read(&in_buffer, data, length);
246	length -= len;
247	rx += len;
248	data = (char*)data + len;
249    }
250    return rx;
251}
252
253static int
254sec_send(int fd, char *from, int length)
255{
256    int bytes;
257    void *buf;
258    bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
259    bytes = htonl(bytes);
260    block_write(fd, &bytes, sizeof(bytes));
261    block_write(fd, buf, ntohl(bytes));
262    free(buf);
263    return length;
264}
265
266int
267sec_fflush(FILE *F)
268{
269    if(data_prot != prot_clear) {
270	if(out_buffer.index > 0){
271	    sec_write(fileno(F), out_buffer.data, out_buffer.index);
272	    out_buffer.index = 0;
273	}
274	sec_send(fileno(F), NULL, 0);
275    }
276    fflush(F);
277    return 0;
278}
279
280int
281sec_write(int fd, char *data, int length)
282{
283    int len = buffer_size;
284    int tx = 0;
285
286    if(data_prot == prot_clear)
287	return write(fd, data, length);
288
289    len -= (*mech->overhead)(app_data, data_prot, len);
290    while(length){
291	if(length < len)
292	    len = length;
293	sec_send(fd, data, len);
294	length -= len;
295	data += len;
296	tx += len;
297    }
298    return tx;
299}
300
301int
302sec_vfprintf2(FILE *f, const char *fmt, va_list ap)
303{
304    char *buf;
305    int ret;
306    if(data_prot == prot_clear)
307	return vfprintf(f, fmt, ap);
308    else {
309	vasprintf(&buf, fmt, ap);
310	ret = buffer_write(&out_buffer, buf, strlen(buf));
311	free(buf);
312	return ret;
313    }
314}
315
316int
317sec_fprintf2(FILE *f, const char *fmt, ...)
318{
319    int ret;
320    va_list ap;
321    va_start(ap, fmt);
322    ret = sec_vfprintf2(f, fmt, ap);
323    va_end(ap);
324    return ret;
325}
326
327int
328sec_putc(int c, FILE *F)
329{
330    char ch = c;
331    if(data_prot == prot_clear)
332	return putc(c, F);
333
334    buffer_write(&out_buffer, &ch, 1);
335    if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
336	sec_write(fileno(F), out_buffer.data, out_buffer.index);
337	out_buffer.index = 0;
338    }
339    return c;
340}
341
342int
343sec_read_msg(char *s, int level)
344{
345    int len;
346    char *buf;
347    int code;
348
349    buf = malloc(strlen(s));
350    len = base64_decode(s + 4, buf); /* XXX */
351
352    len = (*mech->decode)(app_data, buf, len, level);
353    if(len < 0)
354	return -1;
355
356    buf[len] = '\0';
357
358    if(buf[3] == '-')
359	code = 0;
360    else
361	sscanf(buf, "%d", &code);
362    if(buf[len-1] == '\n')
363	buf[len-1] = '\0';
364    strcpy(s, buf);
365    free(buf);
366    return code;
367}
368
369int
370sec_vfprintf(FILE *f, const char *fmt, va_list ap)
371{
372    char *buf;
373    void *enc;
374    int len;
375    if(!sec_complete)
376	return vfprintf(f, fmt, ap);
377
378    vasprintf(&buf, fmt, ap);
379    len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
380    free(buf);
381    if(len < 0) {
382	printf("Failed to encode command.\n");
383	return -1;
384    }
385    if(base64_encode(enc, len, &buf) < 0){
386	printf("Out of memory base64-encoding.\n");
387	return -1;
388    }
389#ifdef FTP_SERVER
390    if(command_prot == prot_safe)
391	fprintf(f, "631 %s\r\n", buf);
392    else if(command_prot == prot_private)
393	fprintf(f, "632 %s\r\n", buf);
394    else if(command_prot == prot_confidential)
395	fprintf(f, "633 %s\r\n", buf);
396#else
397    if(command_prot == prot_safe)
398	fprintf(f, "MIC %s", buf);
399    else if(command_prot == prot_private)
400	fprintf(f, "ENC %s", buf);
401    else if(command_prot == prot_confidential)
402	fprintf(f, "CONF %s", buf);
403#endif
404    free(buf);
405    return 0;
406}
407
408int
409sec_fprintf(FILE *f, const char *fmt, ...)
410{
411    va_list ap;
412    int ret;
413    va_start(ap, fmt);
414    ret = sec_vfprintf(f, fmt, ap);
415    va_end(ap);
416    return ret;
417}
418
419/* end common stuff */
420
421#ifdef FTP_SERVER
422
423void
424auth(char *auth_name)
425{
426    int i;
427    for(i = 0; (mech = mechs[i]) != NULL; i++){
428	if(!strcasecmp(auth_name, mech->name)){
429	    app_data = realloc(app_data, mech->size);
430	    if(mech->init && (*mech->init)(app_data) != 0) {
431		reply(431, "Unable to accept %s at this time", mech->name);
432		return;
433	    }
434	    if(mech->auth) {
435		(*mech->auth)(app_data);
436		return;
437	    }
438	    if(mech->adat)
439		reply(334, "Send authorization data.");
440	    else
441		reply(234, "Authorization complete.");
442	    return;
443	}
444    }
445    free (app_data);
446    reply(504, "%s is unknown to me", auth_name);
447}
448
449void
450adat(char *auth_data)
451{
452    if(mech && !sec_complete) {
453	void *buf = malloc(strlen(auth_data));
454	size_t len;
455	len = base64_decode(auth_data, buf);
456	(*mech->adat)(app_data, buf, len);
457	free(buf);
458    } else
459	reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
460}
461
462void pbsz(int size)
463{
464    size_t new = size;
465    if(!sec_complete)
466	reply(503, "Incomplete security data exchange.");
467    if(mech->pbsz)
468	new = (*mech->pbsz)(app_data, size);
469    if(buffer_size != new){
470	buffer_size = size;
471    }
472    if(new != size)
473	reply(200, "PBSZ=%lu", (unsigned long)new);
474    else
475	reply(200, "OK");
476}
477
478void
479prot(char *pl)
480{
481    int p = -1;
482
483    if(buffer_size == 0){
484	reply(503, "No protection buffer size negotiated.");
485	return;
486    }
487
488    if(!strcasecmp(pl, "C"))
489	p = prot_clear;
490    else if(!strcasecmp(pl, "S"))
491	p = prot_safe;
492    else if(!strcasecmp(pl, "E"))
493	p = prot_confidential;
494    else if(!strcasecmp(pl, "P"))
495	p = prot_private;
496    else {
497	reply(504, "Unrecognized protection level.");
498	return;
499    }
500
501    if(sec_complete){
502	if((*mech->check_prot)(app_data, p)){
503	    reply(536, "%s does not support %s protection.",
504		  mech->name, level_to_name(p));
505	}else{
506	    data_prot = (enum protection_level)p;
507	    reply(200, "Data protection is %s.", level_to_name(p));
508	}
509    }else{
510	reply(503, "Incomplete security data exchange.");
511    }
512}
513
514void ccc(void)
515{
516    if(sec_complete){
517	if(mech->ccc && (*mech->ccc)(app_data) == 0)
518	    command_prot = data_prot = prot_clear;
519	else
520	    reply(534, "You must be joking.");
521    }else
522	reply(503, "Incomplete security data exchange.");
523}
524
525void mec(char *msg, enum protection_level level)
526{
527    void *buf;
528    size_t len;
529    if(!sec_complete) {
530	reply(503, "Incomplete security data exchange.");
531	return;
532    }
533    buf = malloc(strlen(msg) + 2); /* XXX go figure out where that 2
534				      comes from :-) */
535    len = base64_decode(msg, buf);
536    command_prot = level;
537    if(len == (size_t)-1) {
538	reply(501, "Failed to base64-decode command");
539	return;
540    }
541    len = (*mech->decode)(app_data, buf, len, level);
542    if(len == (size_t)-1) {
543	reply(535, "Failed to decode command");
544	return;
545    }
546    ((char*)buf)[len] = '\0';
547    if(strstr((char*)buf, "\r\n") == NULL)
548	strcat((char*)buf, "\r\n");
549    new_ftp_command(buf);
550}
551
552/* ------------------------------------------------------------ */
553
554int
555sec_userok(char *user)
556{
557    if(sec_complete)
558	return (*mech->userok)(app_data, user);
559    return 0;
560}
561
562char *ftp_command;
563
564void
565new_ftp_command(char *command)
566{
567    ftp_command = command;
568}
569
570void
571delete_ftp_command(void)
572{
573    free(ftp_command);
574    ftp_command = NULL;
575}
576
577int
578secure_command(void)
579{
580    return ftp_command != NULL;
581}
582
583enum protection_level
584get_command_prot(void)
585{
586    return command_prot;
587}
588
589#else /* FTP_SERVER */
590
591void
592sec_status(void)
593{
594    if(sec_complete){
595	printf("Using %s for authentication.\n", mech->name);
596	printf("Using %s command channel.\n", level_to_name(command_prot));
597	printf("Using %s data channel.\n", level_to_name(data_prot));
598	if(buffer_size > 0)
599	    printf("Protection buffer size: %lu.\n",
600		   (unsigned long)buffer_size);
601    }else{
602	printf("Not using any security mechanism.\n");
603    }
604}
605
606static int
607sec_prot_internal(int level)
608{
609    int ret;
610    char *p;
611    unsigned int s = 1048576;
612
613    int old_verbose = verbose;
614    verbose = 0;
615
616    if(!sec_complete){
617	printf("No security data exchange has taken place.\n");
618	return -1;
619    }
620
621    if(level){
622	ret = command("PBSZ %u", s);
623	if(ret != COMPLETE){
624	    printf("Failed to set protection buffer size.\n");
625	    return -1;
626	}
627	buffer_size = s;
628	p = strstr(reply_string, "PBSZ=");
629	if(p)
630	    sscanf(p, "PBSZ=%u", &s);
631	if(s < buffer_size)
632	    buffer_size = s;
633    }
634    verbose = old_verbose;
635    ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
636    if(ret != COMPLETE){
637	printf("Failed to set protection level.\n");
638	return -1;
639    }
640
641    data_prot = (enum protection_level)level;
642    return 0;
643}
644
645enum protection_level
646set_command_prot(enum protection_level level)
647{
648    enum protection_level old = command_prot;
649    command_prot = level;
650    return old;
651}
652
653void
654sec_prot(int argc, char **argv)
655{
656    int level = -1;
657
658    if(argc < 2 || argc > 3)
659	goto usage;
660    if(!sec_complete) {
661	printf("No security data exchange has taken place.\n");
662	code = -1;
663	return;
664    }
665    level = name_to_level(argv[argc - 1]);
666
667    if(level == -1)
668	goto usage;
669
670    if((*mech->check_prot)(app_data, level)) {
671	printf("%s does not implement %s protection.\n",
672	       mech->name, level_to_name(level));
673	code = -1;
674	return;
675    }
676
677    if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
678	if(sec_prot_internal(level) < 0){
679	    code = -1;
680	    return;
681	}
682    } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0)
683	set_command_prot(level);
684    else
685	goto usage;
686    code = 0;
687    return;
688 usage:
689    printf("usage: %s [command|data] [clear|safe|confidential|private]\n",
690	   argv[0]);
691    code = -1;
692}
693
694static enum protection_level request_data_prot;
695
696void
697sec_set_protection_level(void)
698{
699    if(sec_complete && data_prot != request_data_prot)
700	sec_prot_internal(request_data_prot);
701}
702
703
704int
705sec_request_prot(char *level)
706{
707    int l = name_to_level(level);
708    if(l == -1)
709	return -1;
710    request_data_prot = (enum protection_level)l;
711    return 0;
712}
713
714int
715sec_login(char *host)
716{
717    int ret;
718    struct sec_client_mech **m;
719    int old_verbose = verbose;
720
721    verbose = -1; /* shut up all messages this will produce (they
722		     are usually not very user friendly) */
723
724    for(m = mechs; *m && (*m)->name; m++) {
725	void *tmp;
726
727	tmp = realloc(app_data, (*m)->size);
728	if (tmp == NULL) {
729	    warnx ("realloc %u failed", (*m)->size);
730	    return -1;
731	}
732	app_data = tmp;
733
734	if((*m)->init && (*(*m)->init)(app_data) != 0) {
735	    printf("Skipping %s...\n", (*m)->name);
736	    continue;
737	}
738	printf("Trying %s...\n", (*m)->name);
739	ret = command("AUTH %s", (*m)->name);
740	if(ret != CONTINUE){
741	    if(code == 504){
742		printf("%s is not supported by the server.\n", (*m)->name);
743	    }else if(code == 534){
744		printf("%s rejected as security mechanism.\n", (*m)->name);
745	    }else if(ret == ERROR) {
746		printf("The server doesn't support the FTP "
747		       "security extensions.\n");
748		verbose = old_verbose;
749		return -1;
750	    }
751	    continue;
752	}
753
754	ret = (*(*m)->auth)(app_data, host);
755
756	if(ret == AUTH_CONTINUE)
757	    continue;
758	else if(ret != AUTH_OK){
759	    /* mechanism is supposed to output error string */
760	    verbose = old_verbose;
761	    return -1;
762	}
763	mech = *m;
764	sec_complete = 1;
765	command_prot = prot_safe;
766	break;
767    }
768
769    verbose = old_verbose;
770    return *m == NULL;
771}
772
773void
774sec_end(void)
775{
776    if (mech != NULL) {
777	if(mech->end)
778	    (*mech->end)(app_data);
779	memset(app_data, 0, mech->size);
780	free(app_data);
781	app_data = NULL;
782    }
783    sec_complete = 0;
784    data_prot = (enum protection_level)0;
785}
786
787#endif /* FTP_SERVER */
788
789