xref: /freebsd/crypto/heimdal/appl/ftp/ftpd/security.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
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 
40 RCSID("$Id: security.c 21225 2007-06-20 10:16:02Z lha $");
41 
42 static enum protection_level command_prot;
43 static enum protection_level data_prot;
44 static size_t buffer_size;
45 
46 struct buffer {
47     void *data;
48     size_t size;
49     size_t index;
50     int eof_flag;
51 };
52 
53 static struct buffer in_buffer, out_buffer;
54 int sec_complete;
55 
56 static 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 
66 static const char *
67 level_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 */
77 static enum protection_level
78 name_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 
90 static 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 
100 static struct sec_server_mech *mech;
101 
102 #else
103 
104 static 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 
114 static struct sec_client_mech *mech;
115 
116 #endif
117 
118 static void *app_data;
119 
120 int
121 sec_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 
132 static int
133 block_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 
149 static int
150 block_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 
164 static int
165 sec_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 
191 static size_t
192 buffer_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 
200 static size_t
201 buffer_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 
219 int
220 sec_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 
257 static int
258 sec_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 
270 int
271 sec_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 
284 int
285 sec_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 
305 int
306 sec_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 
323 int
324 sec_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 
334 int
335 sec_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 
349 int
350 sec_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 
376 int
377 sec_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 
420 int
421 sec_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 
435 int ccc_passed;
436 
437 void
438 auth(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 
472 void
473 adat(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 
485 void 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 
501 void
502 prot(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 
537 void 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 
549 void 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 
578 int
579 sec_userok(char *userstr)
580 {
581     if(sec_complete)
582 	return (*mech->userok)(app_data, userstr);
583     return 0;
584 }
585 
586 int
587 sec_session(char *user)
588 {
589     if(sec_complete && mech->session)
590 	return (*mech->session)(app_data, user);
591     return 0;
592 }
593 
594 char *ftp_command;
595 
596 void
597 new_ftp_command(char *command)
598 {
599     ftp_command = command;
600 }
601 
602 void
603 delete_ftp_command(void)
604 {
605     free(ftp_command);
606     ftp_command = NULL;
607 }
608 
609 int
610 secure_command(void)
611 {
612     return ftp_command != NULL;
613 }
614 
615 enum protection_level
616 get_command_prot(void)
617 {
618     return command_prot;
619 }
620 
621 #else /* FTP_SERVER */
622 
623 void
624 sec_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 
638 static int
639 sec_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 
677 enum protection_level
678 set_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 
693 void
694 sec_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 
742 void
743 sec_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 
782 static enum protection_level request_data_prot;
783 
784 void
785 sec_set_protection_level(void)
786 {
787     if(sec_complete && data_prot != request_data_prot)
788 	sec_prot_internal(request_data_prot);
789 }
790 
791 
792 int
793 sec_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 
802 int
803 sec_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 
866 void
867 sec_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