/***************************************************************************/ /***************************************************************************/ /* */ /* (c) 1995-1999. The Regents of the University of California. All */ /* rights reserved. */ /* */ /* This work was produced at the University of California, Lawrence */ /* Livermore National Laboratory (UC LLNL) under contract no. */ /* W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy */ /* (DOE) and The Regents of the University of California (University) */ /* for the operation of UC LLNL. Copyright is reserved to the */ /* University for purposes of controlled dissemination, */ /* commercialization through formal licensing, or other disposition */ /* under terms of Contract 48; DOE policies, regulations and orders; */ /* and U.S. statutes. The rights of the Federal Government are */ /* reserved under Contract 48 subject to the restrictions agreed upon */ /* by the DOE and University. */ /* */ /* */ /* DISCLAIMER */ /* */ /* This software was prepared as an account of work sponsored by an */ /* agency of the United States Government. Neither the United States */ /* Government nor the University of California nor any of their */ /* employees, makes any warranty, express or implied, or assumes any */ /* liability or responsibility for the accuracy, completeness, or */ /* usefulness of any information, apparatus, product, or process */ /* disclosed, or represents that its specific commercial products, */ /* process, or service by trade name, trademark, manufacturer, or */ /* otherwise, does not necessarily constitute or imply its */ /* endorsement, recommendation, or favoring by the United States */ /* Government or the University of California. The views and opinions */ /* of the authors expressed herein do not necessarily state or reflect */ /* those of the United States Government or the University of */ /* California, and shall not be used for advertising or product */ /* endorsement purposes. */ /* */ /* Permission to use, copy, modify and distribute this software and its */ /* documentation for any non-commercial purpose, without fee, is */ /* hereby granted, provided that the above copyright notice and this */ /* permission notice appear in all copies of the software and */ /* supporting documentation, and that all UC LLNL identification in */ /* the user interface remain unchanged. The title to copyright LLNL */ /* XDIR shall at all times remain with The Regents of the University */ /* of California and users agree to preserve same. Users seeking the */ /* right to make derivative works with LLNL XDIR for commercial */ /* purposes may obtain a license from the Lawrence Livermore National */ /* Laboratory's Technology Transfer Office, P.O. Box 808, L-795, */ /* Livermore, CA 94550. */ /* */ /***************************************************************************/ /***************************************************************************/ #include #include #include #include #include #include #include #include #include #ifdef KERBEROS #include #include #endif #include "xdir.h" #include "str.h" #ifndef FNDELAY #define FNDELAY O_NDELAY #endif extern struct st_host_info hinfo[]; extern int diagnostics; extern int maxt; /* * ftp_open - Open a control connection with the ftp server specified * by "hostname" and "port". "host" is index host structure array. * If successful, the function value is 0 and hinfo[host].ctrl_fd * is set to the file descriptor used for the control connection * socket; returns -6 for stop button pressed; else -1 is returned. * If successful, updates hinfo[host].system and hinfo[host].server. */ ftp_open(host, hostname, port) int host; char *hostname; int port; { int ctrl_fd; struct sockaddr_in remote; int code; char reply[MAXFTPREPLY]; struct hostent *hostptr; char msg[50]; int scode; unsigned long temp; /* Form destination address */ bzero(&remote, (int)sizeof(remote)); temp = inet_addr(hostname); if (temp == ~0L || (sizeof(int) >= 4 && (unsigned int)temp == ~0)) { if ((hostptr = gethostbyname(hostname)) == NULL) return -1; if (hostptr->h_addrtype != AF_INET) return -1; remote.sin_addr.s_addr = ((struct in_addr*)hostptr->h_addr_list[0])->s_addr; } else remote.sin_addr.s_addr = temp; remote.sin_family = AF_INET; remote.sin_port = htons((short)port); if (diagnostics == DEBUG) { sprintf(msg, "*** dest_addr = %lx\n", (unsigned long)remote.sin_addr.s_addr); write_log(msg); } /* Create socket */ if ((ctrl_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { report_perror("Opening socket stream"); return -1; } /* Make socket nonblocking */ #if !defined(UMAXV) && !defined(SOCKS) if (fcntl(ctrl_fd, F_SETFL, FNDELAY) == -1) { report_perror("Making socket stream nonblocking"); close(ctrl_fd); return -1; } #endif /* Connect socket */ if (diagnostics == DEBUG) write_log("*** Will try to connect to remote\n"); if (iconnect(ctrl_fd, (struct sockaddr *)&remote,(int)sizeof(remote)) < 0) { if (errno != EINPROGRESS) { report_perror("Connecting socket stream"); close(ctrl_fd); return -1; } switch (select_on_write(ctrl_fd, maxt)) { case -6: close(ctrl_fd); return -6; case 0: write_log("Timed out while trying to establish connection\n"); case -1: close(ctrl_fd); return -1; } } hinfo[host].ctrl_fd = ctrl_fd; /* Get response from FTP server */ if (diagnostics == DEBUG) write_log("*** Will try to receive welcome message from remote host\n"); scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (scode) { case -6: close(hinfo[host].ctrl_fd); return -6; case 2: determine_server_from_open(host, reply); return 0; case -2: report_client_timeout(host); default: close(hinfo[host].ctrl_fd); if (scode > 0 && diagnostics < VERBOSE) report_ftp_reply(host, reply); return -1; } } /* * ftp_user - Sends USER command to FTP server. "host" is the host id. * Returns 0 if successful, 1 if successful but a password is * required, 2 if successful but a password or a Kerberos * ticket is required, -3 for broken connection, -6 for stop * button pressed, and -1 for other errors. */ ftp_user(host, username) int host; char *username; { char cmd[100]; char reply[MAXFTPREPLY]; int code; int retval; /* Send USER command to FTP server */ sprintf(cmd, "USER %s", username); if ((retval = send_ftp_cmd(host, cmd)) == -2) { report_client_timeout(host); return -1; } else if (retval < 0) return retval; /* Process reply */ retval = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (retval) { case 2: return 0; case 3: #ifdef KERBEROS if (code == 332) return 2; #endif return 1; case -2: report_client_timeout(host); return -1; case -6: abort_ftp_cmd(host); return -6; default: if (retval > 0 && diagnostics < VERBOSE) report_ftp_reply(host, reply); return retval; } } /* * ftp_password - Sends PASS command to FTP server. "host" is the host * id. Returns 0 if successful, 1 if successful but an * account is required, -3 for broken connection, -6 for * stop button pressed, and -1 for other errors. */ ftp_password(host, password) int host; char *password; { char cmd[100]; char reply[MAXFTPREPLY]; int code; int retval; int scode; /* Send PASS command to FTP server */ sprintf(cmd, "PASS %s", password); retval = send_ftp_cmd(host, cmd); bzero(cmd, (int)strlen(cmd)); if (retval == -2) { report_client_timeout(host); return -1; } else if (retval < 0) return retval; /* Process reply */ scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (scode) { case 2: return 0; case 3: return 1; case -3: return -3; case -6: abort_ftp_cmd(host); return -6; case -2: report_client_timeout(host); default: if (scode > 0 && diagnostics < VERBOSE) report_ftp_reply(host, reply); return -1; } } /* * ftp_account - Sends ACCT command to FTP server. "host" is the host id. * Returns 0 if successful, -3 for broken connection, -6 for * stop button pressed, and -1 for other errors. */ ftp_account(host, account) int host; char *account; { char cmd[100]; char reply[MAXFTPREPLY]; int code; int retval; int scode; /* Send ACCT command to FTP server */ sprintf(cmd, "ACCT %s", account); if ((retval = send_ftp_cmd(host, cmd)) == -2) { report_client_timeout(host); return -1; } else if (retval < 0) return retval; /* Process reply */ scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (scode) { case 2: return 0; case -3: return -3; case -6: abort_ftp_cmd(host); return -6; case -2: report_client_timeout(host); default: if (scode > 0 && diagnostics < VERBOSE) report_ftp_reply(host, reply); return -1; } } /* * ftp_passive - Tell server to enter passive mode. If successful, * returns pointer to string containing port address * returned by server in "port", else "port" is undefined. * "host" is the host id. Use XtFree() to release * memory pointed to by "port". Returns 0 if successful, * -3 for broken connection, -6 for stop button pushed, * and -1 for other errors. */ ftp_passive(host, port) int host; char **port; { char reply[MAXFTPREPLY]; int code; int nbytes; char *start; char *end; int retval; /* Send PASV command to FTP server */ if ((retval = send_ftp_cmd(host, "PASV")) == -2) { report_client_timeout(host); return -1; } else if (retval < 0) return retval; /* Get response from FTP server */ retval = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (retval) { case 2: break; case -2: report_client_timeout(host); return -1; case -3: return -3; case -6: abort_ftp_cmd(host); return -6; default: return -1; } /* Extract port address */ if ((start = strchr(reply, '(')) == NULL) return -1; start++; if ((end = strrchr(start, ')')) == NULL) return -1; if ((nbytes = end-start) <= 0) return -1; *port = XtMalloc(nbytes+1); bcopy(start, *port, nbytes); (*port)[nbytes] = '\0'; return 0; } /* * ftp_type - Set file tranfer mode. "host" is the host id. Valid values * for "mode" are ASCII and BINARY. Returns 0 for success, * -3 for broken connection, -6 for stop button pressed, and -1 * for other errors. */ ftp_type(host, mode) int host; int mode; { char reply[MAXFTPREPLY]; char cmd[7]; int code; int retval; char type; int scode; /* Send TYPE command to FTP server */ if (mode == ASCII) type = 'A'; else if (mode == BINARY) type = 'I'; else return -1; sprintf(cmd, "TYPE %c", (char)type); if ((retval = send_ftp_cmd(host, cmd)) == -2) { report_client_timeout(host); return -1; } else if (retval < 0) return retval; /* Get response from FTP server */ scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (scode) { case 2: return 0; case -3: return -3; case -6: abort_ftp_cmd(host); return -6; case -2: report_client_timeout(host); default: if (scode > 0 && diagnostics < VERBOSE) report_ftp_reply(host, reply); return -1; } } /* * ftp_port - Set the data port to be used in data connection to the * specified value. "hostport" is a string of the form * h1,h2,h3,h4,p1,p2 where the hi's are the decimal- * valued bytes of the host's Internet address, and the pi's * are the decimal valued bytes of the port's address. * "host" is the host id. Returns 0 if successful, -3 for * broken connection, -6 for stop button pressed, and -1 for * other errors. */ ftp_port(host, hostport) int host; char *hostport; { char reply[MAXFTPREPLY]; char cmd[37]; int code; int retval; int scode; /* Send PORT command to FTP server */ sprintf(cmd, "PORT %s", hostport); if ((retval = send_ftp_cmd(host, cmd)) == -2) { report_client_timeout(host); return -1; } else if (retval < 0) return retval; /* Get response from FTP server */ scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (scode) { case 2: return 0; case -3: return -3; case -6: abort_ftp_cmd(host); return -6; case -2: report_client_timeout(host); default: if (scode > 0 && diagnostics < VERBOSE) report_ftp_reply(host, reply); return -1; } } /* * ftp_system - Tries to determine the type of system and server the * remote host has by using the low-level SYST command. * Set "print_error_msg" to True if FTP-generated error * messages are to be written to the log when not in verbose * mode. Returns 0 for success, -3 for broken connection, * -6 for stop button pressed, and -1 for other errors. * If successful, and if enough information is returned, * hinfo[host].system and hinfo[host].server are set. */ ftp_system(host, print_error_msg) int host; int print_error_msg; { char reply[MAXFTPREPLY]; int code; int retval; int scode; /* Send SYST command to FTP server */ if ((retval = send_ftp_cmd(host, "SYST")) == -2) { report_client_timeout(host); return -1; } else if (retval < 0) return retval; /* Get response from FTP server */ scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (scode) { case 2: determine_server_from_syst(host, reply); return 0; case -3: return -3; case -6: abort_ftp_cmd(host); return -6; case -2: report_client_timeout(host); default: if (scode > 0 && diagnostics < VERBOSE && print_error_msg) report_ftp_reply(host, reply); return -1; } } /* * ftp_allocate - Tell the server to reserve "size" bytes of space for a * subsequent store command. Returns 0 if successful, * -3 for broken connection, -6 for stop button pushed, * and -1 for other errors. "host" is the host id. */ ftp_allocate(host, size) int host; long size; { char cmd[100]; char reply[MAXFTPREPLY]; int code; int scode; int retval; /* Send ALLO command to FTP server */ if (hinfo[host].server == SERVER_UNIX_HPSS) sprintf(cmd, "ALLO64 %ld", size); else sprintf(cmd, "ALLO %ld", size); if ((retval = send_ftp_cmd(host, cmd)) == -2) { report_client_timeout(host); return -1; } else if (retval < 0) return retval; /* Get response from FTP server */ scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (scode) { case 2: return 0; case -3: return -3; case -6: abort_ftp_cmd(host); return -6; case -2: report_client_timeout(host); default: if (scode > 0 && diagnostics < VERBOSE) report_ftp_reply(host, reply); return -1; } } /* * ftp_kerberos_ticket - Sends TKT command to FTP server. "host" is the * host id. Returns 0 if successful, 1 if successful * but an account is required, -3 for broken * connection, -6 for stop button pressed, and -1 for * other errors. Assumes hinfo[host].hostname has * been set. */ #ifdef KERBEROS ftp_kerberos_ticket(host) int host; { char reply[MAXFTPREPLY]; int code; int retval; int scode; int port_part; char *host_part; krb5_error_code status; krb5_principal server; krb5_principal client; krb5_ccache ccdef; krb5_ap_rep_enc_part *rep_ret; /* Send TKT command to FTP server */ if ((retval = send_ftp_cmd(host, "TKT")) == -2) { report_client_timeout(host); return -1; } else if (retval < 0) return retval; /* Process reply */ scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (scode) { case 2: return 0; case 3: if (code == 333) break; else return -1; case -3: return -3; case -6: abort_ftp_cmd(host); return -6; case -2: report_client_timeout(host); default: if (scode > 0 && diagnostics < VERBOSE) report_ftp_reply(host, reply); return -1; } /* Send authorization request to Kerberos */ krb5_init_ets(); if (parse_hostname(hinfo[host].hostname, &host_part, &port_part)) fatal_error("Bug in ftp_kerberos_ticket()"); krb5_sname_to_principal(host_part, "ftp", KRB5_NT_SRV_HST, &server); XtFree(host_part); if ((status = krb5_cc_default(&ccdef))) { if (diagnostics == DEBUG) { write_log("*** Cannot open credential cache: "); write_log(error_message(status)); write_log("\n"); } return -1; } if ((status = krb5_cc_get_principal( ccdef, &client))){ if (diagnostics == DEBUG) { write_log("*** Cannot get client principal: "); write_log(error_message(status)); write_log("\n"); } return -1; } status = krb5_sendauth((krb5_pointer)&hinfo[host].ctrl_fd, "FTP_V5", client, server, AP_OPTS_MUTUAL_REQUIRED, 0, 0, ccdef, 0, 0, 0, &rep_ret); if (status) { if (diagnostics == DEBUG) { write_log("*** Kerberos authentication failure: "); write_log(error_message(status)); write_log("\n"); } return -1; } krb5_free_ap_rep_enc_part(rep_ret); /* Process reply */ scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt); switch (scode) { case 2: return 0; case 3: return 1; case -3: return -3; case -6: abort_ftp_cmd(host); return -6; case -2: report_client_timeout(host); default: if (scode > 0 && diagnostics < VERBOSE) report_ftp_reply(host, reply); return -1; } } #endif