This commit was manufactured by cvs2svn to create tag
[krb5.git] / src / appl / gssftp / ftp / ftp.c
index a90987f9f53b8e1036edb74b6bb24aa71c9bee31..a00850d95d9730571bc7b3d88efaeffa39f98c00 100644 (file)
  * SUCH DAMAGE.
  */
 
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ * 
+ * All rights reserved.
+ * 
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government.  It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  FundsXpress makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
 #ifndef lint
 static char sccsid[] = "@(#)ftp.c      5.38 (Berkeley) 4/22/91";
 #endif /* not lint */
@@ -41,7 +67,10 @@ static char sccsid[] = "@(#)ftp.c    5.38 (Berkeley) 4/22/91";
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
+#ifndef KRB5_KRB4_COMPAT
+/* krb.h gets this, and Ultrix doesn't protect vs multiple inclusion */
 #include <sys/socket.h>
+#endif
 #include <sys/time.h>
 #include <sys/file.h>
 #ifdef HAVE_SYS_SELECT_H
@@ -58,7 +87,10 @@ static char sccsid[] = "@(#)ftp.c    5.38 (Berkeley) 4/22/91";
 #include <signal.h>
 #include <string.h>
 #include <errno.h>
+#ifndef KRB5_KRB4_COMPAT
+/* krb.h gets this, and Ultrix doesn't protect vs multiple inclusion */
 #include <netdb.h>
+#endif
 #include <fcntl.h>
 #include <pwd.h>
 #ifndef STDARG
@@ -79,21 +111,24 @@ static char sccsid[] = "@(#)ftp.c  5.38 (Berkeley) 4/22/91";
 #define L_INCR 1
 #endif
 
-#ifdef KERBEROS
+#ifdef KRB5_KRB4_COMPAT
 #include <krb.h>
 
 KTEXT_ST ticket;
 CREDENTIALS cred;
 Key_schedule schedule;
 MSG_DAT msg_data;
-#endif /* KERBEROS */
+#endif /* KRB5_KRB4_COMPAT */
 #ifdef GSSAPI
 #include <gssapi/gssapi.h>
+/* need to include the krb5 file, because we're doing manual fallback
+   from the v2 mech to the v2 mech.  Once there's real negotiation,
+   we can be generic again. */
 #include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
 gss_ctx_id_t gcontext;
 #endif /* GSSAPI */
 
-
 static int kerror;     /* XXX needed for all auth types */
 
 char   *auth_type;     /* Authentication succeeded?  If so, what type? */
@@ -155,7 +190,7 @@ hookup(host, port)
                }
                hisctladdr.sin_family = hp->h_addrtype;
                memcpy((caddr_t)&hisctladdr.sin_addr, hp->h_addr_list[0],
-                   hp->h_length);
+                      sizeof(hisctladdr.sin_addr));
                (void) strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf));
        }
        hostname = hostnamebuf;
@@ -177,7 +212,8 @@ hookup(host, port)
                        perror((char *) 0);
                        hp->h_addr_list++;
                        memcpy((caddr_t)&hisctladdr.sin_addr,
-                            hp->h_addr_list[0], hp->h_length);
+                              hp->h_addr_list[0], 
+                              sizeof(hisctladdr.sin_addr));
                        fprintf(stdout, "Trying %s...\n",
                                inet_ntoa(hisctladdr.sin_addr));
                        (void) close(s);
@@ -282,21 +318,32 @@ login(host)
                        user = tmp;
        }
        n = command("USER %s", user);
-       if (n == COMPLETE)
-               n = command("PASS dummy");
+       if (n == COMPLETE) {
+               /* determine if we need to send a dummy password */
+               int oldverbose = verbose;
+
+               verbose = 0;
+               if (command("PWD") != COMPLETE) {
+                       verbose = oldverbose;
+                       command("PASS dummy");
+               } else {
+                       verbose = oldverbose;
+               }
+       }
        else if (n == CONTINUE) {
 #ifndef NOENCRYPTION
-               int oldlevel;
+               int oldclevel;
 #endif
                if (pass == NULL)
                        pass = mygetpass("Password:");
 #ifndef NOENCRYPTION
-               if ((oldlevel = level) == PROT_S) level = PROT_P;
+               oldclevel = clevel;
+               clevel = PROT_P;
 #endif
                n = command("PASS %s", pass);
 #ifndef NOENCRYPTION
                /* level may have changed */
-               if (level == PROT_P) level = oldlevel;
+               if (clevel == PROT_P) clevel = oldclevel;
 #endif
        }
        if (n == CONTINUE) {
@@ -342,14 +389,10 @@ secure_command(cmd)
        char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
        int length;
 
-       if (auth_type) {
-               /*
-                * File protection level also determines whether
-                * commands are MIC or ENC.  Should be independent ...
-                */
-#ifdef KERBEROS
+       if (auth_type && clevel != PROT_C) {
+#ifdef KRB5_KRB4_COMPAT
                if (strcmp(auth_type, "KERBEROS_V4") == 0)
-                   if ((length = level == PROT_P ?
+                   if ((length = clevel == PROT_P ?
                        krb_mk_priv((unsigned char *)cmd, (unsigned char *)out,
                                strlen(cmd), schedule,
                                &cred.session, &myctladdr, &hisctladdr)
@@ -357,37 +400,37 @@ secure_command(cmd)
                                strlen(cmd), &cred.session,
                                &myctladdr, &hisctladdr)) == -1) {
                        fprintf(stderr, "krb_mk_%s failed for KERBEROS_V4\n",
-                                       level == PROT_P ? "priv" : "safe");
+                                       clevel == PROT_P ? "priv" : "safe");
                        return(0);
                    }
-#endif /* KERBEROS */
+#endif /* KRB5_KRB4_COMPAT */
 #ifdef GSSAPI
                /* secure_command (based on level) */
                if (strcmp(auth_type, "GSSAPI") == 0) {
                        gss_buffer_desc in_buf, out_buf;
                        OM_uint32 maj_stat, min_stat;
                        int conf_state;
-/* level = PROT_P; */
+/* clevel = PROT_P; */
                        in_buf.value = cmd;
                        in_buf.length = strlen(cmd) + 1;
                        maj_stat = gss_seal(&min_stat, gcontext,
-                                           (level==PROT_P), /* confidential */
+                                           (clevel==PROT_P), /* private */
                                            GSS_C_QOP_DEFAULT,
                                            &in_buf, &conf_state,
                                            &out_buf);
                        if (maj_stat != GSS_S_COMPLETE) {
                                /* generally need to deal */
                                user_gss_error(maj_stat, min_stat,
-                                              (level==PROT_P)?
+                                              (clevel==PROT_P)?
                                                 "gss_seal ENC didn't complete":
                                                 "gss_seal MIC didn't complete");
-                       } else if ((level == PROT_P) && !conf_state) {
+                       } else if ((clevel == PROT_P) && !conf_state) {
                                fprintf(stderr, 
                                        "GSSAPI didn't encrypt message");
                        } else {
                                if (debug)
                                  fprintf(stderr, "sealed (%s) %d bytes\n",
-                                         level==PROT_P?"ENC":"MIC", 
+                                         clevel==PROT_P?"ENC":"MIC", 
                                          out_buf.length);
                                memcpy(out, out_buf.value, 
                                       length=out_buf.length);
@@ -401,10 +444,10 @@ secure_command(cmd)
                                        radix_error(kerror));
                        return(0);
                }
-               fprintf(cout, "%s %s", level == PROT_P ? "ENC" : "MIC", in);
+               fprintf(cout, "%s %s", clevel == PROT_P ? "ENC" : "MIC", in);
                if(debug) 
                  fprintf(stderr, "secure_command(%s)\nencoding %d bytes %s %s\n",
-                         cmd, length, level==PROT_P ? "ENC" : "MIC", in);
+                         cmd, length, clevel==PROT_P ? "ENC" : "MIC", in);
        } else  fputs(cmd, cout);
        fprintf(cout, "\r\n");
        (void) fflush(cout);
@@ -465,10 +508,10 @@ again:    if (secure_command(in) == 0)
        cpend = 1;
        r = getreply(!strcmp(fmt, "QUIT"));
 #ifndef NOENCRYPTION
-       if (r == 533 && level == PROT_P) {
+       if (r == 533 && clevel == PROT_P) {
                fprintf(stderr,
                        "ENC command not supported at server; retrying under MIC...\n");
-               level = PROT_S;
+               clevel = PROT_S;
                goto again;
        }
 #endif
@@ -498,7 +541,12 @@ getreply(expecteof)
        sigtype cmdabort();
        char ibuf[FTP_BUFSIZ], obuf[FTP_BUFSIZ];
        int safe = 0;
-       extern char *strpbrk(), *strstr();
+#ifndef strpbrk
+       extern char *strpbrk();
+#endif
+#ifndef strstr
+       extern char *strstr();
+#endif
 
        ibuf[0] = '\0';
        if (reply_parse) reply_ptr = reply_buf;
@@ -590,7 +638,7 @@ getreply(expecteof)
                    if (code != 631 && code != 632 && code != 633) {
                        printf("Unknown reply: %d %s\n", code, obuf);
                        n = '5';
-                   } else safe = code;
+                   } else safe = (code == 631);
                if (obuf[0])    /* if there is a string to decode */
                    if (!auth_type) {
                        printf("Cannot decode reply:\n%d %s\n", code, obuf);
@@ -615,7 +663,7 @@ getreply(expecteof)
                                        code, radix_error(kerror), obuf);
                            n = '5';
                        }
-#ifdef KERBEROS
+#ifdef KRB5_KRB4_COMPAT
                        else if (strcmp(auth_type, "KERBEROS_V4") == 0)
                                if ((kerror = safe ?
                                  krb_rd_safe((unsigned char *)ibuf, len,
@@ -631,10 +679,14 @@ getreply(expecteof)
                                        krb_get_err_text(kerror));
                                  n = '5';
                                } else {
-                                 if (verbose) printf("%c:", safe ? 'S' : 'P');
-                                 memcpy(ibuf, msg_data.app_data,
-                                       msg_data.app_length);
-                                 strcpy(&ibuf[msg_data.app_length], "\r\n");
+                                 if (debug) printf("%c:", safe ? 'S' : 'P');
+                                 if(msg_data.app_length < sizeof(ibuf) - 2) {
+                                   memcpy(ibuf, msg_data.app_data,
+                                          msg_data.app_length);
+                                   strcpy(&ibuf[msg_data.app_length], "\r\n");
+                                 } else {
+                                   printf("Message too long!");
+                                 }
                                  continue;
                                }
 #endif
@@ -655,9 +707,14 @@ getreply(expecteof)
                                                 "failed unsealing reply");
                                  n = '5';
                                } else {
-                                 memcpy(ibuf, msg_buf.value, 
-                                        msg_buf.length);
-                                 strcpy(&ibuf[msg_buf.length], "\r\n");
+                                 if(msg_buf.length < sizeof(ibuf) - 2 - 1) {
+                                   memcpy(ibuf, msg_buf.value, 
+                                          msg_buf.length);
+                                   strcpy(&ibuf[msg_buf.length], "\r\n");
+                                 } else {
+                                   user_gss_error(maj_stat, min_stat, 
+                                                  "reply was too long");
+                                 }
                                  gss_release_buffer(&min_stat,&msg_buf);
                                  continue;
                                }
@@ -696,14 +753,14 @@ getreply(expecteof)
 }
 
 empty(mask, sec)
      struct fd_set *mask;
+ fd_set *mask;
        int sec;
 {
        struct timeval t;
 
        t.tv_sec = (long) sec;
        t.tv_usec = 0;
-       return(select(32, mask, (struct fd_set *) 0, (struct fd_set *) 0, &t));
+       return(select(32, mask, (fd_set *) 0, (fd_set *) 0, &t));
 }
 
 jmp_buf        sendabort;
@@ -749,11 +806,11 @@ sendrequest(cmd, local, remote, printnames)
        struct stat st;
        struct timeval start, stop;
        register int c, d;
-       FILE *fin, *dout = 0, *popen();
-       int (*closefunc)(), pclose(), fclose();
-       sig_t oldintr, oldintp;
-       long bytes = 0, hashbytes = HASHBYTES;
-       char *lmode, buf[FTP_BUFSIZ], *bufp;
+       FILE *volatile fin, *volatile dout = 0, *popen();
+       int (*volatile closefunc)(), pclose(), fclose();
+       volatile sig_t oldintr, oldintp;
+       volatile long bytes = 0, hashbytes = HASHBYTES;
+       char *volatile lmode, buf[FTP_BUFSIZ], *bufp;
        sigtype abortsend();
 
        if (verbose && printnames) {
@@ -892,6 +949,8 @@ sendrequest(cmd, local, remote, printnames)
                                }
                                (void) fflush(stdout);
                        }
+                       if (d <= 0 ) 
+  break;
                }
                if (hash && bytes > 0) {
                        if (bytes < HASHBYTES)
@@ -996,17 +1055,17 @@ abortrecv(sig)
 }
 
 recvrequest(cmd, local, remote, lmode, printnames)
-       char *cmd, *local, *remote, *lmode;
+       char *cmd, *volatile local, *remote, *lmode;
 {
-       FILE *fout, *din = 0, *popen();
-       int (*closefunc)(), pclose(), fclose();
-       sig_t oldintr, oldintp;
-       int is_retr, tcrflag, bare_lfs = 0;
+       FILE *volatile fout, *volatile din = 0, *popen();
+       int (*volatile closefunc)(), pclose(), fclose();
+       volatile sig_t oldintr, oldintp;
+       volatile int is_retr, tcrflag, bare_lfs = 0;
        char *gunique();
        static int bufsize;
        static char *buf;
        int blksize;
-       long bytes = 0, hashbytes = HASHBYTES;
+       volatile long bytes = 0, hashbytes = HASHBYTES;
        register int c, d;
        struct timeval start, stop;
        struct stat st;
@@ -1532,11 +1591,12 @@ pswitch(flag)
                char mi[MAXPATHLEN];
                char mo[MAXPATHLEN];
                char *authtype;
-               int lvl;
-#ifdef KERBEROS
+               int clvl;
+               int dlvl;
+#ifdef KRB5_KRB4_COMPAT
                C_Block session;
                Key_schedule schedule;
-#endif /* KERBEROS */
+#endif /* KRB5_KRB4_COMPAT */
        } proxstruct, tmpstruct;
        struct comvars *ip, *op;
 
@@ -1585,32 +1645,40 @@ pswitch(flag)
        mcase = op->mcse;
        ip->ntflg = ntflag;
        ntflag = op->ntflg;
-       (void) strncpy(ip->nti, ntin, 16);
+       (void) strncpy(ip->nti, ntin, sizeof(ip->nti) - 1);
        (ip->nti)[strlen(ip->nti)] = '\0';
-       (void) strcpy(ntin, op->nti);
-       (void) strncpy(ip->nto, ntout, 16);
+       (void) strncpy(ntin, op->nti, sizeof(ntin) - 1);
+       ntin[sizeof(ntin) - 1] = '\0';
+       (void) strncpy(ip->nto, ntout, sizeof(ip->nto) - 1);
        (ip->nto)[strlen(ip->nto)] = '\0';
-       (void) strcpy(ntout, op->nto);
+       (void) strncpy(ntout, op->nto, sizeof(ntout) - 1);
+       ntout[sizeof(ntout) - 1] = '\0';
        ip->mapflg = mapflag;
        mapflag = op->mapflg;
        (void) strncpy(ip->mi, mapin, MAXPATHLEN - 1);
        (ip->mi)[strlen(ip->mi)] = '\0';
-       (void) strcpy(mapin, op->mi);
+       (void) strncpy(mapin, op->mi, sizeof(mapin) - 1);
+       mapin[sizeof(mapin) - 1] = '\0';
        (void) strncpy(ip->mo, mapout, MAXPATHLEN - 1);
        (ip->mo)[strlen(ip->mo)] = '\0';
-       (void) strcpy(mapout, op->mo);
+       (void) strncpy(mapout, op->mo, sizeof(mapout) - 1);
+       mapout[sizeof(mapout) - 1] = '\0';
        ip->authtype = auth_type;
        auth_type = op->authtype;
-       ip->lvl = level;
-       level = op->lvl;
-       if (!level)
-               level = 1;
-#ifdef KERBEROS
+       ip->clvl = clevel;
+       clevel = op->clvl;
+       ip->dlvl = dlevel;
+       dlevel = op->dlvl;
+       if (!clevel)
+            clevel = PROT_C;
+       if (!dlevel)
+            dlevel = PROT_C;
+#ifdef KRB5_KRB4_COMPAT
        memcpy(ip->session, cred.session, sizeof(cred.session));
        memcpy(cred.session, op->session, sizeof(cred.session));
        memcpy(ip->schedule, schedule, sizeof(schedule));
        memcpy(schedule, op->schedule, sizeof(schedule));
-#endif /* KERBEROS */
+#endif /* KRB5_KRB4_COMPAT */
        (void) signal(SIGINT, oldintr);
        if (abrtflag) {
                abrtflag = 0;
@@ -1636,11 +1704,12 @@ abortpt(sig)
 proxtrans(cmd, local, remote)
        char *cmd, *local, *remote;
 {
-       sig_t oldintr;
-       int secndflag = 0, prox_type, nfnd;
+       volatile sig_t oldintr;
+       volatile int secndflag = 0;
+       int prox_type, nfnd;
        extern jmp_buf ptabort;
-       char *cmd2;
-       struct fd_set mask;
+       char *volatile cmd2;
+        fd_set mask;
        sigtype abortpt();
 
        if (strcmp(cmd, "RETR"))
@@ -1755,7 +1824,7 @@ abort:
 
 reset()
 {
-       struct fd_set mask;
+   fd_set mask;
        int nfnd = 1;
 
        FD_ZERO(&mask);
@@ -1790,7 +1859,8 @@ gunique(local)
                fprintf(stderr, "local: %s: %s\n", local, strerror(errno));
                return((char *) 0);
        }
-       (void) strcpy(new, local);
+       (void) strncpy(new, local, sizeof(new) - 3);
+       new[sizeof(new) - 1] = '\0';
        cp = new + strlen(new);
        *cp++ = '.';
        while (!d) {
@@ -1818,98 +1888,45 @@ gunique(local)
        return(new);
 }
 
-#ifdef KERBEROS
+#ifdef KRB5_KRB4_COMPAT
 char realm[REALM_SZ + 1];
-#endif /* KERBEROS */
+#endif /* KRB5_KRB4_COMPAT */
 
 #ifdef GSSAPI
-/* for testing, we don't have an ftp key yet */
-char* gss_services[] = { "ftp", "host", 0 };
+struct {
+    const gss_OID_desc * const * mech_type;
+    char *service_name;
+} gss_trials[] = {
+    { &gss_mech_krb5, "ftp" },
+    { &gss_mech_krb5, "host" },
+};
+int n_gss_trials = sizeof(gss_trials)/sizeof(gss_trials[0]);
 #endif /* GSSAPI */
 
 do_auth()
 {
        extern int setsafe();
-       int oldverbose;
-#ifdef KERBEROS
+       int oldverbose = verbose;
+#ifdef KRB5_KRB4_COMPAT
        char *service, inst[INST_SZ];
        u_long cksum, checksum = (u_long) getpid();
-#endif /* KERBEROS */
-#if defined(KERBEROS) || defined(GSSAPI)
+#endif /* KRB5_KRB4_COMPAT */
+#if defined(KRB5_KRB4_COMPAT) || defined(GSSAPI)
        u_char out_buf[FTP_BUFSIZ];
        int i;
-#endif /* KERBEROS */
+#endif /* KRB5_KRB4_COMPAT */
 
        if (auth_type) return(1);       /* auth already succeeded */
 
        /* Other auth types go here ... */
 
-#ifdef KERBEROS
-       if (command("AUTH %s", "KERBEROS_V4") == CONTINUE) {
-           if (verbose)
-               printf("%s accepted as authentication type\n", "KERBEROS_V4");
-
-           strcpy(inst, (char *) krb_get_phost(hostname));
-           if (realm[0] == '\0')
-               strcpy(realm, (char *) krb_realmofhost(hostname));
-           if ((kerror = krb_mk_req(&ticket, service = "ftp",
-                                       inst, realm, checksum))
-               && (kerror != KDC_PR_UNKNOWN ||
-               (kerror = krb_mk_req(&ticket, service = "rcmd",
-                                       inst, realm, checksum))))
-                       fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n",
-                                       krb_get_err_text(kerror));
-           else if (kerror = krb_get_cred(service, inst, realm, &cred))
-                       fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n",
-                                       krb_get_err_text(kerror));
-           else {
-               key_sched(cred.session, schedule);
-               reply_parse = "ADAT=";
-               oldverbose = verbose;
-               verbose = 0;
-               i = ticket.length;
-               if (kerror = radix_encode(ticket.dat, out_buf, &i, 0))
-                       fprintf(stderr, "Base 64 encoding failed: %s\n",
-                                       radix_error(kerror));
-               else if (command("ADAT %s", out_buf) != COMPLETE)
-                       fprintf(stderr, "Kerberos V4 authentication failed\n");
-               else if (!reply_parse)
-                       fprintf(stderr,
-                              "No authentication data received from server\n");
-               else if (kerror = radix_encode(reply_parse, out_buf, &i, 1))
-                       fprintf(stderr, "Base 64 decoding failed: %s\n",
-                                       radix_error(kerror));
-               else if (kerror = krb_rd_safe(out_buf, i, &cred.session,
-                                           &hisctladdr, &myctladdr, &msg_data))
-                       fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n",
-                                       krb_get_err_text(kerror));
-               else {
-                   /* fetch the (modified) checksum */
-                   (void) memcpy(&cksum, msg_data.app_data, sizeof(cksum));
-                   if (ntohl(cksum) == checksum + 1) {
-                       verbose = oldverbose;
-                       if (verbose)
-                          printf("Kerberos V4 authentication succeeded\n");
-                       reply_parse = NULL;
-                       auth_type = "KERBEROS_V4";
-                       return(1);
-                   } else fprintf(stderr,
-                               "Kerberos V4 mutual authentication failed\n");
-               }
-               verbose = oldverbose;
-               reply_parse = NULL;
-           }
-       } else  fprintf(stderr, "%s rejected as an authentication type\n",
-                               "KERBEROS_V4");
-#endif /* KERBEROS */
 #ifdef GSSAPI
        if (command("AUTH %s", "GSSAPI") == CONTINUE) {
          OM_uint32 maj_stat, min_stat;
          gss_name_t target_name;
          gss_buffer_desc send_tok, recv_tok, *token_ptr;
          char stbuf[FTP_BUFSIZ];
-         char **service_name, **end_service_name;
-         int comcode;
+         int comcode, trial;
          struct gss_channel_bindings_struct chan;
          chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32  */ 
          chan.initiator_address.length = 4;
@@ -1920,21 +1937,15 @@ do_auth()
          chan.application_data.length = 0;
          chan.application_data.value = 0;
 
-         for (end_service_name = gss_services; *end_service_name; )
-           end_service_name++;
-         end_service_name--;
-
          if (verbose)
-           printf("%s accepted as authentication type\n", "GSSAPI");
+           printf("GSSAPI accepted as authentication type\n");
          
          /* blob from gss-client */
-           
          
-         for (service_name = gss_services; *service_name; service_name++) {
-           
+         for (trial = 0; trial < n_gss_trials; trial++) {
            /* ftp@hostname first, the host@hostname */
            /* the V5 GSSAPI binding canonicalizes this for us... */
-           sprintf(stbuf, "%s@%s", *service_name, hostname);
+           sprintf(stbuf, "%s@%s", gss_trials[trial].service_name, hostname);
            if (debug)
              fprintf(stderr, "Trying to authenticate to <%s>\n", stbuf);
 
@@ -1960,8 +1971,9 @@ do_auth()
                                     GSS_C_NO_CREDENTIAL,
                                     &gcontext,
                                     target_name,
-                                    GSS_C_NULL_OID,
-                                    GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+                                    *gss_trials[trial].mech_type,
+                                    GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
+                                    (forward ? GSS_C_DELEG_FLAG : 0),
                                     0,
                                     &chan,     /* channel bindings */
                                     token_ptr,
@@ -1972,7 +1984,8 @@ do_auth()
              
 
              if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED){
-               user_gss_error(maj_stat, min_stat, "initializing context");
+               if (trial == n_gss_trials-1)
+                 user_gss_error(maj_stat, min_stat, "initializing context");
                (void) gss_release_name(&min_stat, &target_name);
                /* could just be that we missed on the service name */
                goto outer_loop;
@@ -1982,16 +1995,28 @@ do_auth()
                int len = send_tok.length;
                reply_parse = "ADAT="; /* for command() later */
                oldverbose = verbose;
-               verbose = 0;
+               verbose = (trial == n_gss_trials-1)?0:-1;
                kerror = radix_encode(send_tok.value, out_buf, &len, 0);
                if (kerror)  {
                  fprintf(stderr, "Base 64 encoding failed: %s\n",
                          radix_error(kerror));
                } else if ((comcode = command("ADAT %s", out_buf))!=COMPLETE
                           /* && comcode != 3 (335)*/) {
-                 fprintf(stderr, "GSSAPI ADAT failed\n");
-                 /* force out of loop */
-                 maj_stat = GSS_S_FAILURE;
+                   if (trial == n_gss_trials-1) {
+                       fprintf(stderr, "GSSAPI ADAT failed\n");
+                       /* force out of loop */
+                       maj_stat = GSS_S_FAILURE;
+                   }
+                   /* backoff to the v1 gssapi is still possible.  Send
+                      a new AUTH command.  If that fails, terminate the
+                      loop */
+                   if (command("AUTH %s", "GSSAPI") != CONTINUE) {
+                       fprintf(stderr,
+                               "GSSAPI ADAT failed, AUTH restart failed\n");
+                       /* force out of loop */
+                       maj_stat = GSS_S_FAILURE;
+                   }
+                   goto outer_loop;
                } else if (!reply_parse) {
                  fprintf(stderr,
                          "No authentication data received from server\n");
@@ -2015,7 +2040,7 @@ do_auth()
 
                /* get out of loop clean */
              gss_complete_loop:
-               service_name = end_service_name;
+               trial = n_gss_trials-1;
                gss_release_buffer(&min_stat, &send_tok);
                gss_release_name(&min_stat, &target_name);
                goto outer_loop;
@@ -2027,8 +2052,7 @@ do_auth()
          }
          verbose = oldverbose;
          if (maj_stat == GSS_S_COMPLETE) {
-           if (verbose)
-             printf("GSSAPI authentication succeeded\n");
+           printf("GSSAPI authentication succeeded\n");
            reply_parse = NULL;
            auth_type = "GSSAPI";
            return(1);
@@ -2039,6 +2063,66 @@ do_auth()
          }
        }
 #endif /* GSSAPI */
+#ifdef KRB5_KRB4_COMPAT
+       if (command("AUTH %s", "KERBEROS_V4") == CONTINUE) {
+           if (verbose)
+               printf("%s accepted as authentication type\n", "KERBEROS_V4");
+
+           strncpy(inst, (char *) krb_get_phost(hostname), sizeof(inst) - 1);
+           inst[sizeof(inst) - 1] = '\0';
+           if (realm[0] == '\0')
+               strncpy(realm, (char *) krb_realmofhost(hostname), sizeof(realm) - 1);
+           realm[sizeof(realm) - 1] = '\0';
+           if ((kerror = krb_mk_req(&ticket, service = "ftp",
+                                       inst, realm, checksum))
+               && (kerror != KDC_PR_UNKNOWN ||
+               (kerror = krb_mk_req(&ticket, service = "rcmd",
+                                       inst, realm, checksum))))
+                       fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n",
+                                       krb_get_err_text(kerror));
+           else if (kerror = krb_get_cred(service, inst, realm, &cred))
+                       fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n",
+                                       krb_get_err_text(kerror));
+           else {
+               key_sched(cred.session, schedule);
+               reply_parse = "ADAT=";
+               oldverbose = verbose;
+               verbose = 0;
+               i = ticket.length;
+               if (kerror = radix_encode(ticket.dat, out_buf, &i, 0))
+                       fprintf(stderr, "Base 64 encoding failed: %s\n",
+                                       radix_error(kerror));
+               else if (command("ADAT %s", out_buf) != COMPLETE)
+                       fprintf(stderr, "Kerberos V4 authentication failed\n");
+               else if (!reply_parse)
+                       fprintf(stderr,
+                              "No authentication data received from server\n");
+               else if (kerror = radix_encode(reply_parse, out_buf, &i, 1))
+                       fprintf(stderr, "Base 64 decoding failed: %s\n",
+                                       radix_error(kerror));
+               else if (kerror = krb_rd_safe(out_buf, i, &cred.session,
+                                           &hisctladdr, &myctladdr, &msg_data))
+                       fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n",
+                                       krb_get_err_text(kerror));
+               else {
+                   /* fetch the (modified) checksum */
+                   (void) memcpy(&cksum, msg_data.app_data, sizeof(cksum));
+                   if (ntohl(cksum) == checksum + 1) {
+                       verbose = oldverbose;
+                       if (verbose)
+                          printf("Kerberos V4 authentication succeeded\n");
+                       reply_parse = NULL;
+                       auth_type = "KERBEROS_V4";
+                       return(1);
+                   } else fprintf(stderr,
+                               "Kerberos V4 mutual authentication failed\n");
+               }
+               verbose = oldverbose;
+               reply_parse = NULL;
+           }
+       } else  fprintf(stderr, "%s rejected as an authentication type\n",
+                               "KERBEROS_V4");
+#endif /* KRB5_KRB4_COMPAT */
 
        /* Other auth types go here ... */
 
@@ -2077,7 +2161,7 @@ FILE *din;
 {
        char buf[FTP_BUFSIZ];
        int nfnd;
-       struct fd_set mask;
+       fd_set mask;
 
        /*
         * send IAC in urgent mode instead of DM because 4.3BSD places oob mark