security.c revision 178825
1/*
2 * Copyright (c) 1998-2002, 2005 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 21225 2007-06-20 10:16:02Z lha $");
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    void *tmp;
170
171    b = block_read(fd, &len, sizeof(len));
172    if (b == 0)
173	return 0;
174    else if (b < 0)
175	return -1;
176    len = ntohl(len);
177    tmp = realloc(buf->data, len);
178    if (tmp == NULL)
179	return -1;
180    buf->data = tmp;
181    b = block_read(fd, buf->data, len);
182    if (b == 0)
183	return 0;
184    else if (b < 0)
185	return -1;
186    buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
187    buf->index = 0;
188    return 0;
189}
190
191static size_t
192buffer_read(struct buffer *buf, void *dataptr, size_t len)
193{
194    len = min(len, buf->size - buf->index);
195    memcpy(dataptr, (char*)buf->data + buf->index, len);
196    buf->index += len;
197    return len;
198}
199
200static size_t
201buffer_write(struct buffer *buf, void *dataptr, size_t len)
202{
203    if(buf->index + len > buf->size) {
204	void *tmp;
205	if(buf->data == NULL)
206	    tmp = malloc(1024);
207	else
208	    tmp = realloc(buf->data, buf->index + len);
209	if(tmp == NULL)
210	    return -1;
211	buf->data = tmp;
212	buf->size = buf->index + len;
213    }
214    memcpy((char*)buf->data + buf->index, dataptr, len);
215    buf->index += len;
216    return len;
217}
218
219int
220sec_read(int fd, void *dataptr, int length)
221{
222    size_t len;
223    int rx = 0;
224
225    if(sec_complete == 0 || data_prot == 0)
226	return read(fd, dataptr, length);
227
228    if(in_buffer.eof_flag){
229	in_buffer.eof_flag = 0;
230	return 0;
231    }
232
233    len = buffer_read(&in_buffer, dataptr, length);
234    length -= len;
235    rx += len;
236    dataptr = (char*)dataptr + len;
237
238    while(length){
239	int ret;
240
241	ret = sec_get_data(fd, &in_buffer, data_prot);
242	if (ret < 0)
243	    return -1;
244	if(ret == 0 && in_buffer.size == 0) {
245	    if(rx)
246		in_buffer.eof_flag = 1;
247	    return rx;
248	}
249	len = buffer_read(&in_buffer, dataptr, length);
250	length -= len;
251	rx += len;
252	dataptr = (char*)dataptr + len;
253    }
254    return rx;
255}
256
257static int
258sec_send(int fd, char *from, int length)
259{
260    int bytes;
261    void *buf;
262    bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
263    bytes = htonl(bytes);
264    block_write(fd, &bytes, sizeof(bytes));
265    block_write(fd, buf, ntohl(bytes));
266    free(buf);
267    return length;
268}
269
270int
271sec_fflush(FILE *F)
272{
273    if(data_prot != prot_clear) {
274	if(out_buffer.index > 0){
275	    sec_write(fileno(F), out_buffer.data, out_buffer.index);
276	    out_buffer.index = 0;
277	}
278	sec_send(fileno(F), NULL, 0);
279    }
280    fflush(F);
281    return 0;
282}
283
284int
285sec_write(int fd, char *dataptr, int length)
286{
287    int len = buffer_size;
288    int tx = 0;
289
290    if(data_prot == prot_clear)
291	return write(fd, dataptr, length);
292
293    len -= (*mech->overhead)(app_data, data_prot, len);
294    while(length){
295	if(length < len)
296	    len = length;
297	sec_send(fd, dataptr, len);
298	length -= len;
299	dataptr += len;
300	tx += len;
301    }
302    return tx;
303}
304
305int
306sec_vfprintf2(FILE *f, const char *fmt, va_list ap)
307{
308    char *buf;
309    int ret;
310    if(data_prot == prot_clear)
311	return vfprintf(f, fmt, ap);
312    else {
313	int len;
314	len = vasprintf(&buf, fmt, ap);
315	if (len == -1)
316	    return len;
317	ret = buffer_write(&out_buffer, buf, len);
318	free(buf);
319	return ret;
320    }
321}
322
323int
324sec_fprintf2(FILE *f, const char *fmt, ...)
325{
326    int ret;
327    va_list ap;
328    va_start(ap, fmt);
329    ret = sec_vfprintf2(f, fmt, ap);
330    va_end(ap);
331    return ret;
332}
333
334int
335sec_putc(int c, FILE *F)
336{
337    char ch = c;
338    if(data_prot == prot_clear)
339	return putc(c, F);
340
341    buffer_write(&out_buffer, &ch, 1);
342    if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
343	sec_write(fileno(F), out_buffer.data, out_buffer.index);
344	out_buffer.index = 0;
345    }
346    return c;
347}
348
349int
350sec_read_msg(char *s, int level)
351{
352    int len;
353    char *buf;
354    int return_code;
355
356    buf = malloc(strlen(s));
357    len = base64_decode(s + 4, buf); /* XXX */
358
359    len = (*mech->decode)(app_data, buf, len, level);
360    if(len < 0)
361	return -1;
362
363    buf[len] = '\0';
364
365    if(buf[3] == '-')
366	return_code = 0;
367    else
368	sscanf(buf, "%d", &return_code);
369    if(buf[len-1] == '\n')
370	buf[len-1] = '\0';
371    strcpy(s, buf);
372    free(buf);
373    return return_code;
374}
375
376int
377sec_vfprintf(FILE *f, const char *fmt, va_list ap)
378{
379    char *buf;
380    void *enc;
381    int len;
382    if(!sec_complete)
383	return vfprintf(f, fmt, ap);
384
385    if (vasprintf(&buf, fmt, ap) == -1) {
386	printf("Failed to allocate command.\n");
387	return -1;
388    }
389    len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
390    free(buf);
391    if(len < 0) {
392	printf("Failed to encode command.\n");
393	return -1;
394    }
395    if(base64_encode(enc, len, &buf) < 0){
396	free(enc);
397	printf("Out of memory base64-encoding.\n");
398	return -1;
399    }
400    free(enc);
401#ifdef FTP_SERVER
402    if(command_prot == prot_safe)
403	fprintf(f, "631 %s\r\n", buf);
404    else if(command_prot == prot_private)
405	fprintf(f, "632 %s\r\n", buf);
406    else if(command_prot == prot_confidential)
407	fprintf(f, "633 %s\r\n", buf);
408#else
409    if(command_prot == prot_safe)
410	fprintf(f, "MIC %s", buf);
411    else if(command_prot == prot_private)
412	fprintf(f, "ENC %s", buf);
413    else if(command_prot == prot_confidential)
414	fprintf(f, "CONF %s", buf);
415#endif
416    free(buf);
417    return 0;
418}
419
420int
421sec_fprintf(FILE *f, const char *fmt, ...)
422{
423    va_list ap;
424    int ret;
425    va_start(ap, fmt);
426    ret = sec_vfprintf(f, fmt, ap);
427    va_end(ap);
428    return ret;
429}
430
431/* end common stuff */
432
433#ifdef FTP_SERVER
434
435int ccc_passed;
436
437void
438auth(char *auth_name)
439{
440    int i;
441    void *tmp;
442
443    for(i = 0; (mech = mechs[i]) != NULL; i++){
444	if(!strcasecmp(auth_name, mech->name)){
445	    tmp = realloc(app_data, mech->size);
446	    if (tmp == NULL) {
447		reply(431, "Unable to accept %s at this time", mech->name);
448		return;
449	    }
450	    app_data = tmp;
451
452	    if(mech->init && (*mech->init)(app_data) != 0) {
453		reply(431, "Unable to accept %s at this time", mech->name);
454		return;
455	    }
456	    if(mech->auth) {
457		(*mech->auth)(app_data);
458		return;
459	    }
460	    if(mech->adat)
461		reply(334, "Send authorization data.");
462	    else
463		reply(234, "Authorization complete.");
464	    return;
465	}
466    }
467    free (app_data);
468    app_data = NULL;
469    reply(504, "%s is unknown to me", auth_name);
470}
471
472void
473adat(char *auth_data)
474{
475    if(mech && !sec_complete) {
476	void *buf = malloc(strlen(auth_data));
477	size_t len;
478	len = base64_decode(auth_data, buf);
479	(*mech->adat)(app_data, buf, len);
480	free(buf);
481    } else
482	reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
483}
484
485void pbsz(int size)
486{
487    size_t new = size;
488    if(!sec_complete)
489	reply(503, "Incomplete security data exchange.");
490    if(mech->pbsz)
491	new = (*mech->pbsz)(app_data, size);
492    if(buffer_size != new){
493	buffer_size = size;
494    }
495    if(new != size)
496	reply(200, "PBSZ=%lu", (unsigned long)new);
497    else
498	reply(200, "OK");
499}
500
501void
502prot(char *pl)
503{
504    int p = -1;
505
506    if(buffer_size == 0){
507	reply(503, "No protection buffer size negotiated.");
508	return;
509    }
510
511    if(!strcasecmp(pl, "C"))
512	p = prot_clear;
513    else if(!strcasecmp(pl, "S"))
514	p = prot_safe;
515    else if(!strcasecmp(pl, "E"))
516	p = prot_confidential;
517    else if(!strcasecmp(pl, "P"))
518	p = prot_private;
519    else {
520	reply(504, "Unrecognized protection level.");
521	return;
522    }
523
524    if(sec_complete){
525	if((*mech->check_prot)(app_data, p)){
526	    reply(536, "%s does not support %s protection.",
527		  mech->name, level_to_name(p));
528	}else{
529	    data_prot = (enum protection_level)p;
530	    reply(200, "Data protection is %s.", level_to_name(p));
531	}
532    }else{
533	reply(503, "Incomplete security data exchange.");
534    }
535}
536
537void ccc(void)
538{
539    if(sec_complete){
540	if(mech->ccc && (*mech->ccc)(app_data) == 0) {
541	    command_prot = data_prot = prot_clear;
542	    ccc_passed = 1;
543	} else
544	    reply(534, "You must be joking.");
545    }else
546	reply(503, "Incomplete security data exchange.");
547}
548
549void mec(char *msg, enum protection_level level)
550{
551    void *buf;
552    size_t len, buf_size;
553    if(!sec_complete) {
554	reply(503, "Incomplete security data exchange.");
555	return;
556    }
557    buf_size = strlen(msg) + 2;
558    buf = malloc(buf_size);
559    len = base64_decode(msg, buf);
560    command_prot = level;
561    if(len == (size_t)-1) {
562	reply(501, "Failed to base64-decode command");
563	return;
564    }
565    len = (*mech->decode)(app_data, buf, len, level);
566    if(len == (size_t)-1) {
567	reply(535, "Failed to decode command");
568	return;
569    }
570    ((char*)buf)[len] = '\0';
571    if(strstr((char*)buf, "\r\n") == NULL)
572	strlcat((char*)buf, "\r\n", buf_size);
573    new_ftp_command(buf);
574}
575
576/* ------------------------------------------------------------ */
577
578int
579sec_userok(char *userstr)
580{
581    if(sec_complete)
582	return (*mech->userok)(app_data, userstr);
583    return 0;
584}
585
586int
587sec_session(char *user)
588{
589    if(sec_complete && mech->session)
590	return (*mech->session)(app_data, user);
591    return 0;
592}
593
594char *ftp_command;
595
596void
597new_ftp_command(char *command)
598{
599    ftp_command = command;
600}
601
602void
603delete_ftp_command(void)
604{
605    free(ftp_command);
606    ftp_command = NULL;
607}
608
609int
610secure_command(void)
611{
612    return ftp_command != NULL;
613}
614
615enum protection_level
616get_command_prot(void)
617{
618    return command_prot;
619}
620
621#else /* FTP_SERVER */
622
623void
624sec_status(void)
625{
626    if(sec_complete){
627	printf("Using %s for authentication.\n", mech->name);
628	printf("Using %s command channel.\n", level_to_name(command_prot));
629	printf("Using %s data channel.\n", level_to_name(data_prot));
630	if(buffer_size > 0)
631	    printf("Protection buffer size: %lu.\n",
632		   (unsigned long)buffer_size);
633    }else{
634	printf("Not using any security mechanism.\n");
635    }
636}
637
638static int
639sec_prot_internal(int level)
640{
641    int ret;
642    char *p;
643    unsigned int s = 1048576;
644
645    int old_verbose = verbose;
646    verbose = 0;
647
648    if(!sec_complete){
649	printf("No security data exchange has taken place.\n");
650	return -1;
651    }
652
653    if(level){
654	ret = command("PBSZ %u", s);
655	if(ret != COMPLETE){
656	    printf("Failed to set protection buffer size.\n");
657	    return -1;
658	}
659	buffer_size = s;
660	p = strstr(reply_string, "PBSZ=");
661	if(p)
662	    sscanf(p, "PBSZ=%u", &s);
663	if(s < buffer_size)
664	    buffer_size = s;
665    }
666    verbose = old_verbose;
667    ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
668    if(ret != COMPLETE){
669	printf("Failed to set protection level.\n");
670	return -1;
671    }
672
673    data_prot = (enum protection_level)level;
674    return 0;
675}
676
677enum protection_level
678set_command_prot(enum protection_level level)
679{
680    int ret;
681    enum protection_level old = command_prot;
682    if(level != command_prot && level == prot_clear) {
683	ret = command("CCC");
684	if(ret != COMPLETE) {
685	    printf("Failed to clear command channel.\n");
686	    return -1;
687	}
688    }
689    command_prot = level;
690    return old;
691}
692
693void
694sec_prot(int argc, char **argv)
695{
696    int level = -1;
697
698    if(argc > 3)
699	goto usage;
700
701    if(argc == 1) {
702	sec_status();
703	return;
704    }
705    if(!sec_complete) {
706	printf("No security data exchange has taken place.\n");
707	code = -1;
708	return;
709    }
710    level = name_to_level(argv[argc - 1]);
711
712    if(level == -1)
713	goto usage;
714
715    if((*mech->check_prot)(app_data, level)) {
716	printf("%s does not implement %s protection.\n",
717	       mech->name, level_to_name(level));
718	code = -1;
719	return;
720    }
721
722    if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
723	if(sec_prot_internal(level) < 0){
724	    code = -1;
725	    return;
726	}
727    } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) {
728	if(set_command_prot(level) < 0) {
729	    code = -1;
730	    return;
731	}
732    } else
733	goto usage;
734    code = 0;
735    return;
736 usage:
737    printf("usage: %s [command|data] [clear|safe|confidential|private]\n",
738	   argv[0]);
739    code = -1;
740}
741
742void
743sec_prot_command(int argc, char **argv)
744{
745    int level;
746
747    if(argc > 2)
748	goto usage;
749
750    if(!sec_complete) {
751	printf("No security data exchange has taken place.\n");
752	code = -1;
753	return;
754    }
755
756    if(argc == 1) {
757	sec_status();
758    } else {
759	level = name_to_level(argv[1]);
760	if(level == -1)
761	    goto usage;
762
763	if((*mech->check_prot)(app_data, level)) {
764	    printf("%s does not implement %s protection.\n",
765		   mech->name, level_to_name(level));
766	    code = -1;
767	    return;
768	}
769	if(set_command_prot(level) < 0) {
770	    code = -1;
771	    return;
772	}
773    }
774    code = 0;
775    return;
776 usage:
777    printf("usage: %s [clear|safe|confidential|private]\n",
778	   argv[0]);
779    code = -1;
780}
781
782static enum protection_level request_data_prot;
783
784void
785sec_set_protection_level(void)
786{
787    if(sec_complete && data_prot != request_data_prot)
788	sec_prot_internal(request_data_prot);
789}
790
791
792int
793sec_request_prot(char *level)
794{
795    int l = name_to_level(level);
796    if(l == -1)
797	return -1;
798    request_data_prot = (enum protection_level)l;
799    return 0;
800}
801
802int
803sec_login(char *host)
804{
805    int ret;
806    struct sec_client_mech **m;
807    int old_verbose = verbose;
808
809    verbose = -1; /* shut up all messages this will produce (they
810		     are usually not very user friendly) */
811
812    for(m = mechs; *m && (*m)->name; m++) {
813	void *tmp;
814
815	tmp = realloc(app_data, (*m)->size);
816	if (tmp == NULL) {
817	    warnx ("realloc %lu failed", (unsigned long)(*m)->size);
818	    return -1;
819	}
820	app_data = tmp;
821
822	if((*m)->init && (*(*m)->init)(app_data) != 0) {
823	    printf("Skipping %s...\n", (*m)->name);
824	    continue;
825	}
826	printf("Trying %s...\n", (*m)->name);
827	ret = command("AUTH %s", (*m)->name);
828	if(ret != CONTINUE){
829	    if(code == 504){
830		printf("%s is not supported by the server.\n", (*m)->name);
831	    }else if(code == 534){
832		printf("%s rejected as security mechanism.\n", (*m)->name);
833	    }else if(ret == ERROR) {
834		printf("The server doesn't support the FTP "
835		       "security extensions.\n");
836		verbose = old_verbose;
837		return -1;
838	    }
839	    continue;
840	}
841
842	ret = (*(*m)->auth)(app_data, host);
843
844	if(ret == AUTH_CONTINUE)
845	    continue;
846	else if(ret != AUTH_OK){
847	    /* mechanism is supposed to output error string */
848	    verbose = old_verbose;
849	    return -1;
850	}
851	mech = *m;
852	sec_complete = 1;
853	if(doencrypt) {
854	    command_prot = prot_private;
855	    request_data_prot = prot_private;
856	} else {
857	    command_prot = prot_safe;
858	}
859	break;
860    }
861
862    verbose = old_verbose;
863    return *m == NULL;
864}
865
866void
867sec_end(void)
868{
869    if (mech != NULL) {
870	if(mech->end)
871	    (*mech->end)(app_data);
872	if (app_data != NULL) {
873	    memset(app_data, 0, mech->size);
874	    free(app_data);
875	    app_data = NULL;
876	}
877    }
878    sec_complete = 0;
879    data_prot = (enum protection_level)0;
880}
881
882#endif /* FTP_SERVER */
883
884