/ appl / ftp / ftp / security.c
security.c
  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$");
 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 prot_invalid;
 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      NULL
 95  };
 96  
 97  static struct sec_server_mech *mech;
 98  
 99  #else
100  
101  static struct sec_client_mech *mechs[] = {
102  #ifdef KRB5
103      &gss_client_mech,
104  #endif
105      NULL
106  };
107  
108  static struct sec_client_mech *mech;
109  
110  #endif
111  
112  static void *app_data;
113  
114  int
115  sec_getc(FILE *F)
116  {
117      if(sec_complete && data_prot) {
118  	char c;
119  	if(sec_read(fileno(F), &c, 1) <= 0)
120  	    return EOF;
121  	return c;
122      } else
123  	return getc(F);
124  }
125  
126  static int
127  block_read(int fd, void *buf, size_t len)
128  {
129      unsigned char *p = buf;
130      int b;
131      while(len) {
132  	b = read(fd, p, len);
133  	if (b == 0)
134  	    return 0;
135  	else if (b < 0)
136  	    return -1;
137  	len -= b;
138  	p += b;
139      }
140      return p - (unsigned char*)buf;
141  }
142  
143  static int
144  block_write(int fd, void *buf, size_t len)
145  {
146      unsigned char *p = buf;
147      int b;
148      while(len) {
149  	b = write(fd, p, len);
150  	if(b < 0)
151  	    return -1;
152  	len -= b;
153  	p += b;
154      }
155      return p - (unsigned char*)buf;
156  }
157  
158  static int
159  sec_get_data(int fd, struct buffer *buf, int level)
160  {
161      int len;
162      int b;
163      void *tmp;
164  
165      b = block_read(fd, &len, sizeof(len));
166      if (b == 0)
167  	return 0;
168      else if (b < 0)
169  	return -1;
170      len = ntohl(len);
171      tmp = realloc(buf->data, len);
172      if (tmp == NULL)
173  	return -1;
174      buf->data = tmp;
175      b = block_read(fd, buf->data, len);
176      if (b == 0)
177  	return 0;
178      else if (b < 0)
179  	return -1;
180      buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
181      buf->index = 0;
182      return 0;
183  }
184  
185  static size_t
186  buffer_read(struct buffer *buf, void *dataptr, size_t len)
187  {
188      len = min(len, buf->size - buf->index);
189      memcpy(dataptr, (char*)buf->data + buf->index, len);
190      buf->index += len;
191      return len;
192  }
193  
194  static size_t
195  buffer_write(struct buffer *buf, void *dataptr, size_t len)
196  {
197      if(buf->index + len > buf->size) {
198  	void *tmp;
199  	if(buf->data == NULL)
200  	    tmp = malloc(1024);
201  	else
202  	    tmp = realloc(buf->data, buf->index + len);
203  	if(tmp == NULL)
204  	    return -1;
205  	buf->data = tmp;
206  	buf->size = buf->index + len;
207      }
208      memcpy((char*)buf->data + buf->index, dataptr, len);
209      buf->index += len;
210      return len;
211  }
212  
213  int
214  sec_read(int fd, void *dataptr, int length)
215  {
216      size_t len;
217      int rx = 0;
218  
219      if(sec_complete == 0 || data_prot == 0)
220  	return read(fd, dataptr, length);
221  
222      if(in_buffer.eof_flag){
223  	in_buffer.eof_flag = 0;
224  	return 0;
225      }
226  
227      len = buffer_read(&in_buffer, dataptr, length);
228      length -= len;
229      rx += len;
230      dataptr = (char*)dataptr + len;
231  
232      while(length){
233  	int ret;
234  
235  	ret = sec_get_data(fd, &in_buffer, data_prot);
236  	if (ret < 0)
237  	    return -1;
238  	if(ret == 0 && in_buffer.size == 0) {
239  	    if(rx)
240  		in_buffer.eof_flag = 1;
241  	    return rx;
242  	}
243  	len = buffer_read(&in_buffer, dataptr, length);
244  	length -= len;
245  	rx += len;
246  	dataptr = (char*)dataptr + len;
247      }
248      return rx;
249  }
250  
251  static int
252  sec_send(int fd, char *from, int length)
253  {
254      int bytes;
255      void *buf;
256      bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
257      bytes = htonl(bytes);
258      block_write(fd, &bytes, sizeof(bytes));
259      block_write(fd, buf, ntohl(bytes));
260      free(buf);
261      return length;
262  }
263  
264  int
265  sec_fflush(FILE *F)
266  {
267      if(data_prot != prot_clear) {
268  	if(out_buffer.index > 0){
269  	    sec_write(fileno(F), out_buffer.data, out_buffer.index);
270  	    out_buffer.index = 0;
271  	}
272  	sec_send(fileno(F), NULL, 0);
273      }
274      fflush(F);
275      return 0;
276  }
277  
278  int
279  sec_write(int fd, char *dataptr, int length)
280  {
281      int len = buffer_size;
282      int tx = 0;
283  
284      if(data_prot == prot_clear)
285  	return write(fd, dataptr, length);
286  
287      len -= (*mech->overhead)(app_data, data_prot, len);
288      while(length){
289  	if(length < len)
290  	    len = length;
291  	sec_send(fd, dataptr, len);
292  	length -= len;
293  	dataptr += len;
294  	tx += len;
295      }
296      return tx;
297  }
298  
299  int
300  sec_vfprintf2(FILE *f, const char *fmt, va_list ap)
301  {
302      char *buf;
303      int ret;
304      if(data_prot == prot_clear)
305  	return vfprintf(f, fmt, ap);
306      else {
307  	int len;
308  	len = vasprintf(&buf, fmt, ap);
309  	if (len == -1)
310  	    return len;
311  	ret = buffer_write(&out_buffer, buf, len);
312  	free(buf);
313  	return ret;
314      }
315  }
316  
317  int
318  sec_fprintf2(FILE *f, const char *fmt, ...)
319  {
320      int ret;
321      va_list ap;
322      va_start(ap, fmt);
323      ret = sec_vfprintf2(f, fmt, ap);
324      va_end(ap);
325      return ret;
326  }
327  
328  int
329  sec_putc(int c, FILE *F)
330  {
331      char ch = c;
332      if(data_prot == prot_clear)
333  	return putc(c, F);
334  
335      buffer_write(&out_buffer, &ch, 1);
336      if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
337  	sec_write(fileno(F), out_buffer.data, out_buffer.index);
338  	out_buffer.index = 0;
339      }
340      return c;
341  }
342  
343  int
344  sec_read_msg(char *s, int level)
345  {
346      int len;
347      char *buf;
348      int return_code;
349  
350      buf = malloc(strlen(s));
351      len = base64_decode(s + 4, buf); /* XXX */
352  
353      len = (*mech->decode)(app_data, buf, len, level);
354      if(len < 0)
355  	return -1;
356  
357      buf[len] = '\0';
358  
359      if(buf[3] == '-')
360  	return_code = 0;
361      else
362  	sscanf(buf, "%d", &return_code);
363      if(buf[len-1] == '\n')
364  	buf[len-1] = '\0';
365      strcpy(s, buf);
366      free(buf);
367      return return_code;
368  }
369  
370  int
371  sec_vfprintf(FILE *f, const char *fmt, va_list ap)
372  {
373      char *buf;
374      void *enc;
375      int len;
376      if(!sec_complete)
377  	return vfprintf(f, fmt, ap);
378  
379      if (vasprintf(&buf, fmt, ap) == -1) {
380  	printf("Failed to allocate command.\n");
381  	return -1;
382      }
383      len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
384      free(buf);
385      if(len < 0) {
386  	printf("Failed to encode command.\n");
387  	return -1;
388      }
389      if(base64_encode(enc, len, &buf) < 0){
390  	free(enc);
391  	printf("Out of memory base64-encoding.\n");
392  	return -1;
393      }
394      free(enc);
395  #ifdef FTP_SERVER
396      if(command_prot == prot_safe)
397  	fprintf(f, "631 %s\r\n", buf);
398      else if(command_prot == prot_private)
399  	fprintf(f, "632 %s\r\n", buf);
400      else if(command_prot == prot_confidential)
401  	fprintf(f, "633 %s\r\n", buf);
402  #else
403      if(command_prot == prot_safe)
404  	fprintf(f, "MIC %s", buf);
405      else if(command_prot == prot_private)
406  	fprintf(f, "ENC %s", buf);
407      else if(command_prot == prot_confidential)
408  	fprintf(f, "CONF %s", buf);
409  #endif
410      free(buf);
411      return 0;
412  }
413  
414  int
415  sec_fprintf(FILE *f, const char *fmt, ...)
416  {
417      va_list ap;
418      int ret;
419      va_start(ap, fmt);
420      ret = sec_vfprintf(f, fmt, ap);
421      va_end(ap);
422      return ret;
423  }
424  
425  /* end common stuff */
426  
427  #ifdef FTP_SERVER
428  
429  int ccc_passed;
430  
431  void
432  auth(char *auth_name)
433  {
434      int i;
435      void *tmp;
436  
437      for(i = 0; (mech = mechs[i]) != NULL; i++){
438  	if(!strcasecmp(auth_name, mech->name)){
439  	    tmp = realloc(app_data, mech->size);
440  	    if (tmp == NULL) {
441  		reply(431, "Unable to accept %s at this time", mech->name);
442  		return;
443  	    }
444  	    app_data = tmp;
445  
446  	    if(mech->init && (*mech->init)(app_data) != 0) {
447  		reply(431, "Unable to accept %s at this time", mech->name);
448  		return;
449  	    }
450  	    if(mech->auth) {
451  		(*mech->auth)(app_data);
452  		return;
453  	    }
454  	    if(mech->adat)
455  		reply(334, "Send authorization data.");
456  	    else
457  		reply(234, "Authorization complete.");
458  	    return;
459  	}
460      }
461      free (app_data);
462      app_data = NULL;
463      reply(504, "%s is unknown to me", auth_name);
464  }
465  
466  void
467  adat(char *auth_data)
468  {
469      if(mech && !sec_complete) {
470  	void *buf = malloc(strlen(auth_data));
471  	size_t len;
472  	len = base64_decode(auth_data, buf);
473  	(*mech->adat)(app_data, buf, len);
474  	free(buf);
475      } else
476  	reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
477  }
478  
479  void pbsz(int size)
480  {
481      size_t new = size;
482      if(!sec_complete)
483  	reply(503, "Incomplete security data exchange.");
484      if(mech->pbsz)
485  	new = (*mech->pbsz)(app_data, size);
486      if(buffer_size != new){
487  	buffer_size = size;
488      }
489      if(new != size)
490  	reply(200, "PBSZ=%lu", (unsigned long)new);
491      else
492  	reply(200, "OK");
493  }
494  
495  void
496  prot(char *pl)
497  {
498      int p = -1;
499  
500      if(buffer_size == 0){
501  	reply(503, "No protection buffer size negotiated.");
502  	return;
503      }
504  
505      if(!strcasecmp(pl, "C"))
506  	p = prot_clear;
507      else if(!strcasecmp(pl, "S"))
508  	p = prot_safe;
509      else if(!strcasecmp(pl, "E"))
510  	p = prot_confidential;
511      else if(!strcasecmp(pl, "P"))
512  	p = prot_private;
513      else {
514  	reply(504, "Unrecognized protection level.");
515  	return;
516      }
517  
518      if(sec_complete){
519  	if((*mech->check_prot)(app_data, p)){
520  	    reply(536, "%s does not support %s protection.",
521  		  mech->name, level_to_name(p));
522  	}else{
523  	    data_prot = (enum protection_level)p;
524  	    reply(200, "Data protection is %s.", level_to_name(p));
525  	}
526      }else{
527  	reply(503, "Incomplete security data exchange.");
528      }
529  }
530  
531  void ccc(void)
532  {
533      if(sec_complete){
534  	if(mech->ccc && (*mech->ccc)(app_data) == 0) {
535  	    command_prot = data_prot = prot_clear;
536  	    ccc_passed = 1;
537  	} else
538  	    reply(534, "You must be joking.");
539      }else
540  	reply(503, "Incomplete security data exchange.");
541  }
542  
543  void mec(char *msg, enum protection_level level)
544  {
545      void *buf;
546      size_t len, buf_size;
547      if(!sec_complete) {
548  	reply(503, "Incomplete security data exchange.");
549  	return;
550      }
551      buf_size = strlen(msg) + 2;
552      buf = malloc(buf_size);
553      if (buf == NULL) {
554  	reply(501, "Failed to allocate %lu", (unsigned long)buf_size);
555  	return;
556      }
557      len = base64_decode(msg, buf);
558      command_prot = level;
559      if(len == (size_t)-1) {
560  	free(buf);
561  	reply(501, "Failed to base64-decode command");
562  	return;
563      }
564      len = (*mech->decode)(app_data, buf, len, level);
565      if(len == (size_t)-1) {
566  	free(buf);
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 prot_invalid;
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