This commit was manufactured by cvs2svn to create tag
[krb5.git] / src / appl / gssftp / ftp / ftp.c
index 66f1a1f26f8b20bb48ac94ff2664fc040f1afac9..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 */
@@ -95,11 +121,14 @@ MSG_DAT msg_data;
 #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? */
@@ -289,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) {
@@ -349,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 ...
-                */
+       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)
@@ -364,7 +400,7 @@ 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 /* KRB5_KRB4_COMPAT */
@@ -374,27 +410,27 @@ secure_command(cmd)
                        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);
@@ -408,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);
@@ -472,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
@@ -505,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;
@@ -639,9 +680,13 @@ getreply(expecteof)
                                  n = '5';
                                } else {
                                  if (debug) printf("%c:", safe ? 'S' : 'P');
-                                 memcpy(ibuf, msg_data.app_data,
-                                       msg_data.app_length);
-                                 strcpy(&ibuf[msg_data.app_length], "\r\n");
+                                 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
@@ -662,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;
                                }
@@ -756,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) {
@@ -1005,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;
@@ -1541,7 +1591,8 @@ pswitch(flag)
                char mi[MAXPATHLEN];
                char mo[MAXPATHLEN];
                char *authtype;
-               int lvl;
+               int clvl;
+               int dlvl;
 #ifdef KRB5_KRB4_COMPAT
                C_Block session;
                Key_schedule schedule;
@@ -1594,26 +1645,34 @@ 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;
+       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));
@@ -1645,10 +1704,11 @@ 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;
+       char *volatile cmd2;
         fd_set mask;
        sigtype abortpt();
 
@@ -1799,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) {
@@ -1832,8 +1893,14 @@ char realm[REALM_SZ + 1];
 #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()
@@ -1859,8 +1926,7 @@ do_auth()
          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;
@@ -1871,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);
 
@@ -1911,7 +1971,7 @@ do_auth()
                                     GSS_C_NO_CREDENTIAL,
                                     &gcontext,
                                     target_name,
-                                    GSS_C_NULL_OID,
+                                    *gss_trials[trial].mech_type,
                                     GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
                                     (forward ? GSS_C_DELEG_FLAG : 0),
                                     0,
@@ -1924,7 +1984,7 @@ do_auth()
              
 
              if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED){
-               if (service_name == end_service_name)
+               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 */
@@ -1935,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");
@@ -1968,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;
@@ -1980,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);
@@ -1997,9 +2068,11 @@ do_auth()
            if (verbose)
                printf("%s accepted as authentication type\n", "KERBEROS_V4");
 
-           strcpy(inst, (char *) krb_get_phost(hostname));
+           strncpy(inst, (char *) krb_get_phost(hostname), sizeof(inst) - 1);
+           inst[sizeof(inst) - 1] = '\0';
            if (realm[0] == '\0')
-               strcpy(realm, (char *) krb_realmofhost(hostname));
+               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 ||