diff --git a/appl/ftp/ftp/Makefile.in b/appl/ftp/ftp/Makefile.in index 71b2d105e..f37135f71 100644 --- a/appl/ftp/ftp/Makefile.in +++ b/appl/ftp/ftp/Makefile.in @@ -20,7 +20,7 @@ libdir = $(exec_prefix)/lib ATHENA = /usr/athena -ftp_OBJS = cmds.o cmdtab.o ftp.o main.o ruserpass.o domacro.o globals.o +ftp_OBJS = cmds.o cmdtab.o ftp.o krb4.o main.o ruserpass.o domacro.o globals.o kauth.o all: ftp diff --git a/appl/ftp/ftp/cmdtab.c b/appl/ftp/ftp/cmdtab.c index fbcb0d377..21bc4001b 100644 --- a/appl/ftp/ftp/cmdtab.c +++ b/appl/ftp/ftp/cmdtab.c @@ -106,6 +106,9 @@ char umaskhelp[] = "get (set) umask on remote side"; char userhelp[] = "send new user information"; char verbosehelp[] = "toggle verbose mode"; +char prothelp[] = "set protection level"; +char kauthhelp[] = "get remote tokens"; + struct cmd cmdtab[] = { { "!", shellhelp, 0, 0, 0, shell }, { "$", domachelp, 1, 0, 0, domacro }, @@ -179,6 +182,10 @@ struct cmd cmdtab[] = { { "umask", umaskhelp, 0, 1, 1, do_umask }, { "verbose", verbosehelp, 0, 0, 0, setverbose }, { "?", helphelp, 0, 0, 1, help }, + + { "prot", prothelp, 0, 1, 0, sec_prot }, + { "kauth", kauthhelp, 0, 1, 0, kauth }, + { 0 }, }; diff --git a/appl/ftp/ftp/extern.h b/appl/ftp/ftp/extern.h index 686d6a6b2..d906018d6 100644 --- a/appl/ftp/ftp/extern.h +++ b/appl/ftp/ftp/extern.h @@ -165,3 +165,5 @@ extern int proxy; extern char reply_string[]; extern off_t restart_point; extern int NCMDS; + +extern char username[32]; diff --git a/appl/ftp/ftp/ftp.c b/appl/ftp/ftp/ftp.c index 3de9c0eeb..b738ca474 100644 --- a/appl/ftp/ftp/ftp.c +++ b/appl/ftp/ftp/ftp.c @@ -35,8 +35,6 @@ #include "ftp_locl.h" - - struct sockaddr_in hisctladdr; struct sockaddr_in data_addr; int data = -1; @@ -159,77 +157,6 @@ bad: return ((char *)0); } -#include -#include - -int krb4_auth = 0; -KTEXT_ST krb4_adat; - -static des_cblock key; -static des_key_schedule schedule; - -int do_auth(char *service, char *host) -{ - int ret; - CREDENTIALS cred; - char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ]; - strcpy(sname, service); - strcpy(inst, krb_get_phost(host)); - strcpy(realm, krb_realmofhost(host)); - ret = krb_mk_req(&krb4_adat, sname, inst, realm, 0); - if(ret) - return ret; - strcpy(sname, service); - strcpy(inst, krb_get_phost(host)); - strcpy(realm, krb_realmofhost(host)); - ret = krb_get_cred(sname, inst, realm, &cred); - memmove(&key, &cred.session, sizeof(des_cblock)); - des_key_sched(&key, schedule); - memset(&cred, 0, sizeof(cred)); - return ret; -} - - -int do_klogin(char *host) -{ - int ret; - char *phost; - char *p, *q; - int len; - char *serv = "ftp"; - char adat[1024]; - MSG_DAT msg_data; - int checksum; - - ret = command("AUTH KERBEROS_V4"); - if(ret == CONTINUE){ - ret = do_auth("ftp", host); - if(ret == KDC_PR_UNKNOWN) - ret = do_auth("rcmd", host); - if(ret) - return ret; - base64_encode(krb4_adat.dat, krb4_adat.length, &p); - ret = command("ADAT %s", p); - free(p); - if(ret == COMPLETE){ - p = strstr(reply_string, "ADAT="); - if(p){ - p+=5; - for(q = p; isalnum(*q) || strchr("/+=", *q); q++); - *q = 0; - len = base64_decode(p, adat); - ret = krb_rd_safe(adat, len, &key, - &hisctladdr, &myctladdr, &msg_data); - memmove(&checksum, msg_data.app_data, 4); - checksum = ntohl(checksum); - } - krb4_auth = 1; - return 0; - } - } - return -1; -} - int login(char *host) { @@ -263,13 +190,17 @@ login(char *host) user = tmp; } if(strcmp(user, "ftp") && strcmp(user, "anonymous")){ - do_klogin(host); + if(do_klogin(host) < 0) + fprintf(stderr, "Resorting to plaintext user and password.\n"); } + strcpy(username, user); n = command("USER %s", user); if (n == CONTINUE) { - if (pass == NULL) - pass = getpass("Password:"); - n = command("PASS %s", pass); + if(auth_complete) + pass = user; + else if (pass == NULL) + pass = getpass("Password:"); + n = command("PASS %s", pass); } if (n == CONTINUE) { aflag++; @@ -306,69 +237,6 @@ cmdabort(int sig) longjmp(ptabort,1); } -int krb4_write_enc(FILE *F, char *fmt, va_list ap) -{ - int len; - char *p; - char buf[1024]; - char enc[1024]; - vsprintf(buf, fmt, ap); - len = krb_mk_priv(buf, enc, strlen(buf), schedule, &key, - &myctladdr, &hisctladdr); - base64_encode(enc, len, &p); - - fprintf(F, "ENC %s", p); - return 0; -} - - -int krb4_read_msg(char *s, int priv) -{ - int len; - int ret; - char buf[1024]; - MSG_DAT m; - int code; - - len = base64_decode(s + 4, buf); - if(priv) - ret = krb_rd_priv(buf, len, schedule, &key, - &hisctladdr, &myctladdr, &m); - else - ret = krb_rd_safe(buf, len, &key, &myctladdr, &hisctladdr, &m); - if(ret){ - fprintf(stderr, "%s\n", krb_get_err_text(ret)); - return -1; - } - - m.app_data[m.app_length] = 0; - if(m.app_data[3] == '-') - code = 0; - else - sscanf((char*)m.app_data, "%d", &code); - strncpy(s, (char*)m.app_data, strlen((char*)m.app_data)); - - s[m.app_length] = 0; - len = strlen(s); - if(s[len-1] == '\n') - s[len-1] = 0; - - return code; -} - -int -krb4_read_mic(char *s) -{ - return krb4_read_msg(s, 0); -} - -int -krb4_read_enc(char *s) -{ - return krb4_read_msg(s, 1); -} - - int command(char *fmt, ...) { @@ -391,7 +259,7 @@ command(char *fmt, ...) printf("PASS XXXX"); else vfprintf(stdout, fmt, ap); - if(krb4_auth) + if(auth_complete) krb4_write_enc(cout, fmt, ap); else vfprintf(cout, fmt, ap); @@ -464,8 +332,8 @@ getreply(int expecteof) sscanf(buf, "%d", &code); fprintf(stdout, "P:"); }else if(code == 633){ - fprintf(stdout, "Confidentiality is meaningless:/n"); - }else if(krb4_auth) + fprintf(stdout, "Confidentiality is meaningless:\n"); + }else if(auth_complete) fprintf(stdout, "!!"); /* clear text */ fprintf(stdout, "%s\n", buf); if(buf[3] == ' '){ @@ -484,7 +352,7 @@ getreply(int expecteof) return code / 100; } }else{ - if(krb4_auth) + if(auth_complete) fprintf(stdout, "!!"); fprintf(stdout, "%s\n", buf); } @@ -589,7 +457,7 @@ getreply(int expecteof) continue; } *cp = '\0'; - if(krb4_auth){ + if(auth_complete){ if(code == 631) krb4_read_mic(reply_string); else @@ -784,8 +652,8 @@ sendrequest(char *cmd, char *local, char *remote, int printnames) while ((c = read(fileno(fin), buf, sizeof (buf))) > 0) { bytes += c; for (bufp = buf; c > 0; c -= d, bufp += d) - if ((d = write(fileno(dout), bufp, c)) <= 0) - break; + if ((d = sec_write(fileno(dout), bufp, c)) <= 0) + break; if (hash) { while (bytes >= hashbytes) { (void) putchar('#'); @@ -794,6 +662,7 @@ sendrequest(char *cmd, char *local, char *remote, int printnames) (void) fflush(stdout); } } + sec_fflush(dout); if (hash && bytes > 0) { if (bytes < HASHBYTES) (void) putchar('#'); @@ -810,39 +679,36 @@ sendrequest(char *cmd, char *local, char *remote, int printnames) break; case TYPE_A: - while ((c = getc(fin)) != EOF) { - if (c == '\n') { - while (hash && (bytes >= hashbytes)) { - (void) putchar('#'); - (void) fflush(stdout); - hashbytes += HASHBYTES; - } - if (ferror(dout)) - break; - (void) putc('\r', dout); - bytes++; - } - (void) putc(c, dout); - bytes++; - /* if (c == '\r') { */ - /* (void) putc('\0', dout); // this violates rfc */ - /* bytes++; */ - /* } */ - } - if (hash) { - if (bytes < hashbytes) - (void) putchar('#'); - (void) putchar('\n'); + while ((c = getc(fin)) != EOF) { + if (c == '\n') { + while (hash && (bytes >= hashbytes)) { + (void) putchar('#'); (void) fflush(stdout); + hashbytes += HASHBYTES; + } + if (ferror(dout)) + break; + (void) sec_putc('\r', dout); + bytes++; } - if (ferror(fin)) - warn("local: %s", local); - if (ferror(dout)) { - if (errno != EPIPE) - warn("netout"); - bytes = -1; - } - break; + sec_putc(c, dout); + bytes++; + } + sec_fflush(dout); + if (hash) { + if (bytes < hashbytes) + (void) putchar('#'); + (void) putchar('\n'); + (void) fflush(stdout); + } + if (ferror(fin)) + warn("local: %s", local); + if (ferror(dout)) { + if (errno != EPIPE) + warn("netout"); + bytes = -1; + } + break; } if (closefunc != NULL) (*closefunc)(fin); @@ -1048,17 +914,17 @@ recvrequest(char *cmd, char *local, char *remote, char *lmode, int printnames) return; } errno = d = 0; - while ((c = read(fileno(din), buf, bufsize)) > 0) { - if ((d = write(fileno(fout), buf, c)) != c) - break; - bytes += c; - if (hash) { - while (bytes >= hashbytes) { - (void) putchar('#'); - hashbytes += HASHBYTES; - } - (void) fflush(stdout); + while ((c = sec_read(fileno(din), buf, bufsize)) > 0) { + if ((d = write(fileno(fout), buf, c)) != c) + break; + bytes += c; + if (hash) { + while (bytes >= hashbytes) { + (void) putchar('#'); + hashbytes += HASHBYTES; } + (void) fflush(stdout); + } } if (hash && bytes > 0) { if (bytes < HASHBYTES) @@ -1087,7 +953,7 @@ recvrequest(char *cmd, char *local, char *remote, char *lmode, int printnames) goto done; n = restart_point; for (i = 0; i++ < n;) { - if ((ch = getc(fout)) == EOF) + if ((ch = sec_getc(fout)) == EOF) goto done; if (ch == '\n') i++; @@ -1100,7 +966,8 @@ done: return; } } - while ((c = getc(din)) != EOF) { + + while ((c = sec_getc(din)) != EOF) { if (c == '\n') bare_lfs++; while (c == '\r') { @@ -1110,7 +977,7 @@ done: hashbytes += HASHBYTES; } bytes++; - if ((c = getc(din)) != '\n' || tcrflag) { + if ((c = sec_getc(din)) != '\n' || tcrflag) { if (ferror(fout)) goto break2; (void) putc('\r', fout); diff --git a/appl/ftp/ftp/ftp_locl.h b/appl/ftp/ftp/ftp_locl.h index 6c4ada391..9f2070fa9 100644 --- a/appl/ftp/ftp/ftp_locl.h +++ b/appl/ftp/ftp/ftp_locl.h @@ -26,12 +26,15 @@ #include #include #include +#include #include "extern.h" #include "common.h" #include "ftp_var.h" #include "pathnames.h" +#include "krb4.h" + #if defined(__sun__) && !defined(__svr4) int fclose(FILE*); int pclose(FILE*); diff --git a/appl/ftp/ftp/ftp_var.h b/appl/ftp/ftp/ftp_var.h index 5a5db4ea8..61846bf8f 100644 --- a/appl/ftp/ftp/ftp_var.h +++ b/appl/ftp/ftp/ftp_var.h @@ -127,3 +127,5 @@ struct macel { extern int macnum; /* number of defined macros */ extern struct macel macros[16]; extern char macbuf[4096]; + + diff --git a/appl/ftp/ftp/globals.c b/appl/ftp/ftp/globals.c index ee56439ff..10096df51 100644 --- a/appl/ftp/ftp/globals.c +++ b/appl/ftp/ftp/globals.c @@ -69,3 +69,5 @@ int options; /* used during socket creation */ int macnum; /* number of defined macros */ struct macel macros[16]; char macbuf[4096]; + +char username[32]; diff --git a/appl/ftp/ftp/kauth.c b/appl/ftp/ftp/kauth.c new file mode 100644 index 000000000..adfc69b40 --- /dev/null +++ b/appl/ftp/ftp/kauth.c @@ -0,0 +1,75 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "ftp_locl.h" +#include + +void kauth(int argc, char **argv) +{ + int ret; + char buf[1024]; + des_cblock key; + des_key_schedule schedule; + KTEXT_ST tkt; + char *name; + char *p; + + + if(argc > 2){ + printf("usage: %s [principal]\n", argv[0]); + code = -1; + return; + } + if(argc == 2) + name = argv[1]; + else + name = username; + ret = command("SITE KAUTH %s", name); + if(ret != CONTINUE){ + code = -1; + return; + } + p = strstr(reply_string, "T="); + if(!p){ + printf("Bad reply from server.\n"); + code = -1; + return; + } + p += 2; + tkt.length = base64_decode(p, &tkt.dat); + if(tkt.length < 0){ + printf("Failed to decode base64 in reply.\n"); + code = -1; + return; + } + + p = strstr(reply_string, "P="); + if(!p){ + printf("Bad reply from server.\n"); + code = -1; + return; + } + name = p + 2; + for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++); + *p = 0; + + + sprintf(buf, "Password for %s:", name); + des_read_password(&key, buf, 0); + + des_set_key(&key, schedule); + + des_pcbc_encrypt((des_cblock*)tkt.dat, (des_cblock*)tkt.dat, tkt.length, + schedule, &key, DES_DECRYPT); + memset(key, 0, sizeof(key)); + memset(schedule, 0, sizeof(schedule)); + base64_encode(tkt.dat, tkt.length, &p); + ret = command("SITE KAUTH %s %s", name, p); + free(p); + if(ret != COMPLETE){ + code = -1; + return; + } + code = 0; +} diff --git a/appl/ftp/ftp/krb4.c b/appl/ftp/ftp/krb4.c new file mode 100644 index 000000000..fe5b398d8 --- /dev/null +++ b/appl/ftp/ftp/krb4.c @@ -0,0 +1,421 @@ + +#include "ftp_locl.h" + +#include +#include + +KTEXT_ST krb4_adat; + +static des_cblock key; +static des_key_schedule schedule; + +static char *data_buffer; + +enum { prot_clear, prot_safe, prot_confidential, prot_private }; + +extern struct sockaddr_in hisctladdr, myctladdr; + +int auth_complete; + +static int command_prot; + +static int auth_pbsz; +static int data_prot; + +void sec_prot(int argc, char **argv) +{ + int s; + int ret; + char *p; + int level = -1; + if(argc != 2){ + fprintf(stderr, "foo?\n"); + code = -1; + return; + } + if(!auth_complete){ + fprintf(stderr, "ehu?\n"); + code = -1; + return; + } + if(!strcmp(argv[1], "clear")) + level = prot_clear; + + if(!strcmp(argv[1], "safe")) + level = prot_safe; + + if(!strcmp(argv[1], "private")) + level = prot_private; + + if(level == -1){ + fprintf(stderr, "ehu?\n"); + code = -1; + return; + } + if(level){ + s = 65536; + ret = command("PBSZ %d", s); + if(ret != COMPLETE){ + fprintf(stderr, "Ehu?\n"); + code = -1; + return; + } + auth_pbsz = s; + p = strstr(reply_string, "PBSZ="); + if(p) + sscanf(p, "PBSZ=%d", &s); + if(s < auth_pbsz) + auth_pbsz = s; + if(data_buffer) + free(data_buffer); + data_buffer = malloc(auth_pbsz); + } + + ret = command("PROT %c", level["CSEP"]); /* XXX :-) */ + if(ret != COMPLETE){ + fprintf(stderr, "Ehu?\n"); + code = -1; + return; + } + data_prot = level; + code = 0; +} + + +int sec_getc(FILE *F) +{ + if(auth_complete && data_prot) + return krb4_getc(F); + else + return getc(F); +} + +int sec_read(int fd, void *data, int length) +{ + if(auth_complete && data_prot) + return krb4_read(fd, data, length); + else + return read(fd, data, length); +} + +static int +krb4_recv(int fd) +{ + int len; + MSG_DAT m; + int kerror; + + krb_net_read(fd, &len, sizeof(len)); + len = ntohl(len); + krb_net_read(fd, data_buffer, len); + if(data_prot == prot_safe) + kerror = krb_rd_safe(data_buffer, len, &key, + &hisctladdr, &myctladdr, &m); + else + kerror = krb_rd_priv(data_buffer, len, schedule, &key, + &hisctladdr, &myctladdr, &m); + if(kerror){ + return -1; + } + memmove(data_buffer, m.app_data, m.app_length); + return m.app_length; +} + + +int krb4_getc(FILE *F) +{ + static int bytes; + static int index; + if(bytes == 0){ + bytes = krb4_recv(fileno(F)); + index = 0; + } + if(bytes){ + bytes--; + return data_buffer[index++]; + } + return EOF; +} + +int krb4_read(int fd, char *data, int length) +{ + static int left; + static int index; + static int eof; + int len = left; + int rx = 0; + + if(eof){ + eof = 0; + return 0; + } + + if(left){ + if(length < len) + len = length; + memmove(data, data_buffer + index, len); + length -= len; + index += len; + rx += len; + left -= len; + } + + while(length){ + len = krb4_recv(fd); + if(len == 0){ + if(rx) + eof = 1; + return rx; + } + if(len > length){ + left = len - length; + len = index = length; + } + memmove(data, data_buffer, len); + length -= len; + data += len; + rx += len; + } + return rx; +} + + +static int +krb4_encode(char *from, char *to, int length) +{ + if(data_prot == prot_safe) + return krb_mk_safe(from, to, length, &key, + &myctladdr, &hisctladdr); + else + return krb_mk_priv(from, to, length, schedule, &key, + &myctladdr, &hisctladdr); +} + +static int +krb4_overhead(int len) +{ + if(data_prot == prot_safe) + return 31; + else + return 26; +} + +static char p_buf[1024]; +static int p_index; + +int +sec_putc(int c, FILE *F) +{ + if(data_prot){ + if((c == '\n' && p_index) || p_index == sizeof(p_buf)){ + sec_write(fileno(F), p_buf, p_index); + p_index = 0; + } + p_buf[p_index++] = c; + return c; + } + return putc(c, F); +} + +static int +sec_send(int fd, char *from, int length) +{ + int bytes; + bytes = krb4_encode(from, data_buffer, length); + bytes = htonl(bytes); + krb_net_write(fd, &bytes, sizeof(bytes)); + krb_net_write(fd, data_buffer, ntohl(bytes)); + return length; +} + +int +sec_fflush(FILE *F) +{ + if(data_prot){ + if(index){ + sec_write(fileno(F), p_buf, p_index); + p_index = 0; + } + sec_send(fileno(F), NULL, 0); + } + fflush(F); + return 0; +} + +int +sec_write(int fd, char *data, int length) +{ + int len = auth_pbsz; + int tx = 0; + int bytes; + + if(data_prot == prot_clear) + return write(fd, data, length); + + len -= krb4_overhead(len); + while(length){ + if(length < len) + len = length; + sec_send(fd, data, len); + length -= len; + data += len; + tx += len; + } + return tx; +} + +static int +do_auth(char *service, char *host, int checksum) +{ + int ret; + CREDENTIALS cred; + char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ]; + strcpy(sname, service); + strcpy(inst, krb_get_phost(host)); + strcpy(realm, krb_realmofhost(host)); + ret = krb_mk_req(&krb4_adat, sname, inst, realm, checksum); + if(ret) + return ret; + strcpy(sname, service); + strcpy(inst, krb_get_phost(host)); + strcpy(realm, krb_realmofhost(host)); + ret = krb_get_cred(sname, inst, realm, &cred); + memmove(&key, &cred.session, sizeof(des_cblock)); + des_key_sched(&key, schedule); + memset(&cred, 0, sizeof(cred)); + return ret; +} + + +int +do_klogin(char *host) +{ + int ret; + char *phost; + char *p, *q; + int len; + char adat[1024]; + MSG_DAT msg_data; + int checksum; + int tmp; + + ret = command("AUTH KERBEROS_V4"); + if(ret != CONTINUE){ + if(code == 504){ + fprintf(stderr, "Kerberos is not supported by the server.\n"); + }else if(code == 534){ + fprintf(stderr, "KERBEROS_V4 rejected as security mechanism.\n"); + }else if(ret == ERROR) + fprintf(stderr, "The server doesn't understand the FTP " + "security extentions.\n"); + return -1; + } + + checksum = getpid(); + ret = do_auth("ftp", host, checksum); + if(ret == KDC_PR_UNKNOWN) + ret = do_auth("rcmd", host, checksum); + if(ret){ + fprintf(stderr, "%s\n", krb_get_err_text(ret)); + return ret; + } + + base64_encode(krb4_adat.dat, krb4_adat.length, &p); + ret = command("ADAT %s", p); + free(p); + + if(ret != COMPLETE){ + fprintf(stderr, "Server didn't accept auth data."); + return -1; + } + + p = strstr(reply_string, "ADAT="); + if(!p){ + fprintf(stderr, "Remote host didn't send adat reply."); + return -1; + } + p+=5; + len = base64_decode(p, adat); + if(len < 0){ + fprintf(stderr, "Failed to decode base64 from server."); + return -1; + } + ret = krb_rd_safe(adat, len, &key, + &hisctladdr, &myctladdr, &msg_data); + if(ret){ + fprintf(stderr, "Error reading reply from server: %s.", + krb_get_err_text(ret)); + return -1; + } + memmove(&tmp, msg_data.app_data, 4); + tmp = ntohl(tmp); + if(tmp - checksum != 1){ + fprintf(stderr, "Bad checksum returned from server."); + return -1; + } + auth_complete = 1; + return 0; +} + +int krb4_write_enc(FILE *F, char *fmt, va_list ap) +{ + int len; + char *p; + char buf[1024]; + char enc[1024]; + vsprintf(buf, fmt, ap); + len = krb_mk_priv(buf, enc, strlen(buf), schedule, &key, + &myctladdr, &hisctladdr); + base64_encode(enc, len, &p); + + fprintf(F, "ENC %s", p); + return 0; +} + + +int krb4_read_msg(char *s, int priv) +{ + int len; + int ret; + char buf[1024]; + MSG_DAT m; + int code; + + len = base64_decode(s + 4, buf); + if(priv) + ret = krb_rd_priv(buf, len, schedule, &key, + &hisctladdr, &myctladdr, &m); + else + ret = krb_rd_safe(buf, len, &key, &myctladdr, &hisctladdr, &m); + if(ret){ + fprintf(stderr, "%s\n", krb_get_err_text(ret)); + return -1; + } + + m.app_data[m.app_length] = 0; + if(m.app_data[3] == '-') + code = 0; + else + sscanf((char*)m.app_data, "%d", &code); + strncpy(s, (char*)m.app_data, strlen((char*)m.app_data)); + + s[m.app_length] = 0; + len = strlen(s); + if(s[len-1] == '\n') + s[len-1] = 0; + + return code; +} + +int +krb4_read_mic(char *s) +{ + return krb4_read_msg(s, 0); +} + +int +krb4_read_enc(char *s) +{ + return krb4_read_msg(s, 1); +} + diff --git a/appl/ftp/ftp/krb4.h b/appl/ftp/ftp/krb4.h new file mode 100644 index 000000000..a49fb6b2f --- /dev/null +++ b/appl/ftp/ftp/krb4.h @@ -0,0 +1,10 @@ +#ifndef __KRB4_H__ +#define __KRB4_H__ + +extern int auth_complete; + +void sec_prot(int, char**); + +void kauth(int, char **); + +#endif /* __KRB4_H__ */ diff --git a/appl/ftp/ftp/main.c b/appl/ftp/ftp/main.c index e44167fc4..51b41f9d0 100644 --- a/appl/ftp/ftp/main.c +++ b/appl/ftp/ftp/main.c @@ -38,7 +38,7 @@ #include "ftp_locl.h" #ifndef HAVE___PROGNAME -char *__progname; +char *__progname = "ftp"; #endif int @@ -55,10 +55,6 @@ main(int argc, char **argv) interactive = 1; autologin = 1; -#ifndef HAVE___PROGNAME - __progname = argv[0]; -#endif - while ((ch = getopt(argc, argv, "dgintv")) != EOF) { switch (ch) { case 'd': diff --git a/appl/ftp/ftpd/Makefile.in b/appl/ftp/ftpd/Makefile.in index 3de323c44..3ba061622 100644 --- a/appl/ftp/ftpd/Makefile.in +++ b/appl/ftp/ftpd/Makefile.in @@ -19,18 +19,18 @@ libdir = $(exec_prefix)/lib ATHENA = /usr/athena -ftpd_OBJS = ftpd.o ftpcmd.o logwtmp.o popen.o auth.o krb4.o +ftpd_OBJS = ftpd.o ftpcmd.o logwtmp.o popen.o auth.o krb4.o kauth.o all: ftpd .c.o: - $(CC) -c $(CFLAGS) -I.. -I$(srcdir)/.. -I../common -I$(srcdir)/../common -I$(ATHENA)/include $(DEFS) $< + $(CC) -c $(CFLAGS) -I. -I$(srcdir) -I.. -I$(srcdir)/.. -I../common -I$(srcdir)/../common -I$(ATHENA)/include $(DEFS) $< install: ftpd: $(ftpd_OBJS) ../common/libcommon.a - $(CC) -o ftpd $(ftpd_OBJS) ../common/libcommon.a -L$(ATHENA)/lib -lkrb -ldes + $(CC) -o ftpd $(ftpd_OBJS) ../common/libcommon.a -L$(ATHENA)/lib -lkafs -lkrb -ldes ftpcmd.c: ftpcmd.y $(YACC) $(YFLAGS) $< diff --git a/appl/ftp/ftpd/auth.c b/appl/ftp/ftpd/auth.c index 589a25c54..0f7b4b157 100644 --- a/appl/ftp/ftpd/auth.c +++ b/appl/ftp/ftpd/auth.c @@ -12,18 +12,26 @@ #include "auth.h" static struct at auth_types [] = { - { "KERBEROS_V4", krb4_auth, krb4_adat, krb4_pbsz, krb4_prot, krb4_ccc, - krb4_mic, krb4_conf, krb4_enc, krb4_userok, krb4_vprintf }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0 } + { "KERBEROS_V4", krb4_auth, krb4_adat, krb4_pbsz, krb4_prot, krb4_ccc, + krb4_mic, krb4_conf, krb4_enc, krb4_read, krb4_write, krb4_userok, + krb4_vprintf }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; struct at *ct; int data_protection; int buffer_size; +unsigned char *data_buffer; int auth_complete; +char *protection_names[] = { + "clear", "safe", + "confidential", "private" +}; + + void auth_init(void) { } @@ -33,99 +41,154 @@ int prot_level; void new_ftp_command(char *command) { - ftp_command = command; + ftp_command = command; } void delete_ftp_command(void) { - if(ftp_command){ - free(ftp_command); - ftp_command = NULL; - } + if(ftp_command){ + free(ftp_command); + ftp_command = NULL; + } } void auth(char *auth) { - for(ct=auth_types; ct->name; ct++){ - if(!strcmp(auth, ct->name)){ - ct->auth(auth); - return; + for(ct=auth_types; ct->name; ct++){ + if(!strcasecmp(auth, ct->name)){ + ct->auth(auth); + return; + } } - } - reply(504, "%s is not a known security mechanism", auth); + reply(504, "%s is not a known security mechanism", auth); } void adat(char *auth) { - if(ct) - ct->adat(auth); - else - reply(503, "Error, error"); + if(ct && !auth_complete) + ct->adat(auth); + else + reply(503, "You must (re)issue an AUTH first."); } void pbsz(int size) { - if(ct) - ct->pbsz(size); - else - reply(503, "Error, error"); + int old = buffer_size; + if(ct && auth_complete) + ct->pbsz(size); + else + reply(503, "Incomplete security data exchange."); + if(buffer_size != old){ + if(data_buffer) + free(data_buffer); + data_buffer = malloc(buffer_size + 4); + } } void prot(char *pl) { - if(ct) - ct->prot(pl); - else - reply(503, "Error, error"); + int p = -1; + + if(buffer_size == 0){ + reply(503, "No protection buffer size negotiated."); + return; + } + + if(!strcasecmp(pl, "C")) + p = prot_clear; + + if(!strcasecmp(pl, "S")) + p = prot_safe; + + if(!strcasecmp(pl, "E")) + p = prot_confidential; + + if(!strcasecmp(pl, "P")) + p = prot_private; + + if(p == -1){ + reply(504, "Unrecognized protection level."); + return; + } + + if(ct && auth_complete){ + if(ct->prot(p)){ + reply(536, "%s does not support %s protection.", + ct->name, protection_names[p]); + }else{ + data_protection = p; + reply(200, "Data protection is %s.", + protection_names[data_protection]); + } + }else{ + reply(503, "Incomplete security data exchange."); + } } void ccc(void) { - if(ct) - ct->ccc(); - else - reply(503, "Error, error"); + if(ct && auth_complete){ + if(!ct->ccc()) + prot_level = prot_clear; + }else + reply(503, "Incomplete security data exchange."); } void mic(char *msg) { - prot_level = prot_safe; - if(ct) - ct->mic(msg); - else - reply(500, "Command unrecognized"); + if(ct && auth_complete){ + if(!ct->mic(msg)) + prot_level = prot_safe; + }else + reply(503, "Incomplete security data exchange."); } void conf(char *msg) { - prot_level = prot_confidential; - if(ct) - ct->conf(msg); - else - reply(500, "Command unrecognized"); + if(ct && auth_complete){ + if(!ct->conf(msg)) + prot_level = prot_confidential; + }else + reply(503, "Incomplete security data exchange."); } void enc(char *msg) { - prot_level = prot_private; - if(ct) - ct->enc(msg); - else - reply(500, "Command unrecognized"); + if(ct && auth_complete){ + if(!ct->enc(msg)) + prot_level = prot_private; + }else + reply(503, "Incomplete security data exchange."); +} + +int auth_read(int fd, void *data, int length) +{ + if(ct && auth_complete && data_protection) + return ct->read(fd, data, length); + else + return read(fd, data, length); +} + +int auth_write(int fd, void *data, int length) +{ + if(ct && auth_complete && data_protection) + return ct->write(fd, data, length); + else + return write(fd, data, length); } void auth_vprintf(const char *fmt, va_list ap) { - if(ct && auth_complete && prot_level){ - ct->vprintf(fmt, ap); - }else - vprintf(fmt, ap); + if(ct && auth_complete && prot_level){ + ct->vprintf(fmt, ap); + }else + vprintf(fmt, ap); } void auth_printf(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - auth_vprintf(fmt, ap); - va_end(ap); + va_list ap; + va_start(ap, fmt); + auth_vprintf(fmt, ap); + va_end(ap); } diff --git a/appl/ftp/ftpd/auth.h b/appl/ftp/ftpd/auth.h index 8f4b5bad0..9eb91a5ff 100644 --- a/appl/ftp/ftpd/auth.h +++ b/appl/ftp/ftpd/auth.h @@ -1,5 +1,5 @@ -#ifndef _AUTH_H_ -#define _AUTH_H_ +#ifndef __AUTH_H__ +#define __AUTH_H__ #include @@ -8,27 +8,32 @@ struct at { int (*auth)(char*); int (*adat)(char*); int (*pbsz)(int); - int (*prot)(char*); + int (*prot)(int); int (*ccc)(void); int (*mic)(char*); int (*conf)(char*); int (*enc)(char*); + int (*read)(int, void*, int); + int (*write)(int, void*, int); int (*userok)(char*); int (*vprintf)(const char*, va_list); }; -struct at *ct; +extern struct at *ct; enum protection_levels { prot_clear, prot_safe, prot_confidential, prot_private }; +extern char *protection_names[]; + extern char *ftp_command; extern int prot_level; -int data_protection; -int buffer_size; -int auth_complete; +extern int data_protection; +extern int buffer_size; +extern unsigned char *data_buffer; +extern int auth_complete; void auth_init(void); @@ -41,9 +46,12 @@ void mic(char*); void conf(char*); void enc(char*); +int auth_read(int, void*, int); +int auth_write(int, void*, int); + void auth_vprintf(const char *fmt, va_list ap); void auth_printf(const char *fmt, ...); void new_ftp_command(char *command); -#endif /* _AUTH_H_ */ +#endif /* __AUTH_H__ */ diff --git a/appl/ftp/ftpd/extern.h b/appl/ftp/ftpd/extern.h index 416f6a7a0..7d4bbdad9 100644 --- a/appl/ftp/ftpd/extern.h +++ b/appl/ftp/ftpd/extern.h @@ -77,6 +77,8 @@ void upper(char *); void user(char *); void yyerror(char *); +void kauth(char *, char*); + extern struct sockaddr_in ctrl_addr, his_addr; extern char hostname[]; @@ -102,5 +104,4 @@ extern int usedefault; extern int transflag; extern char tmpline[]; - #endif /* _EXTERN_H_ */ diff --git a/appl/ftp/ftpd/ftpcmd.y b/appl/ftp/ftpd/ftpcmd.y index 391685816..8c4dc9767 100644 --- a/appl/ftp/ftpd/ftpcmd.y +++ b/appl/ftp/ftpd/ftpcmd.y @@ -107,6 +107,8 @@ char *fromname; AUTH ADAT PROT PBSZ CCC MIC CONF ENC + KAUTH + LEXERR %token STRING @@ -475,6 +477,24 @@ cmd timeout); } } + + | SITE SP KAUTH SP STRING CRLF + { + char *p; + size_t s; + + p = strpbrk($5, " \t"); + if(p){ + *p++ = 0; + s = strspn(p, " \t"); + if(s >= 0) + kauth($5, p + s); + else + kauth($5, p); + }else + kauth($5, NULL); + free($5); + } | STOU check_login SP pathname CRLF { if ($2 && $4 != NULL) @@ -841,7 +861,7 @@ struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "AUTH", AUTH, STR1, 1, " auth-type" }, { "ADAT", ADAT, STR1, 1, " auth-data" }, { "PBSZ", PBSZ, ARGS, 1, " buffer-size" }, - { "PROT", PROT, ARGS, 1, " prot-level" }, + { "PROT", PROT, STR1, 1, " prot-level" }, { "CCC", CCC, ARGS, 1, "" }, { "MIC", MIC, STR1, 1, " integrity command" }, { "CONF", CONF, STR1, 1, " confidentiality command" }, @@ -855,6 +875,9 @@ struct tab sitetab[] = { { "IDLE", IDLE, ARGS, 1, "[ maximum-idle-time ]" }, { "CHMOD", CHMOD, NSTR, 1, " mode file-name" }, { "HELP", HELP, OSTR, 1, "[ ]" }, + + { "KAUTH", KAUTH, STR1, 1, " principal [ ticket ]" }, + { NULL, 0, 0, 0, 0 } }; @@ -867,9 +890,7 @@ static void toolong __P((int)); static int yylex __P((void)); static struct tab * -lookup(p, cmd) - struct tab *p; - char *cmd; +lookup(struct tab *p, char *cmd) { for (; p->name != NULL; p++) @@ -958,8 +979,7 @@ getline(char *s, int n) } static void -toolong(signo) - int signo; +toolong(int signo) { reply(421, @@ -971,7 +991,7 @@ toolong(signo) } static int -yylex() +yylex(void) { static int cpos, state; char *cp, *cp2; @@ -1189,8 +1209,7 @@ yylex() } void -upper(s) - char *s; +upper(char *s) { while (*s != '\0') { if (islower(*s)) @@ -1200,8 +1219,7 @@ upper(s) } static char * -copy(s) - char *s; +copy(char *s) { char *p; @@ -1213,9 +1231,7 @@ copy(s) } static void -help(ctab, s) - struct tab *ctab; - char *s; +help(struct tab *ctab, char *s) { struct tab *c; int width, NCMDS; @@ -1278,8 +1294,7 @@ help(ctab, s) } static void -sizecmd(filename) - char *filename; +sizecmd(char *filename) { switch (type) { case TYPE_L: diff --git a/appl/ftp/ftpd/ftpd.c b/appl/ftp/ftpd/ftpd.c index 15092edce..f9dfcc90c 100644 --- a/appl/ftp/ftpd/ftpd.c +++ b/appl/ftp/ftpd/ftpd.c @@ -226,23 +226,23 @@ curdir(void) static void conn_wait(void) { - int s, t; - struct sockaddr_in sa; - int one = 1; - s = socket(AF_INET, SOCK_STREAM, 0); + int s, t; + struct sockaddr_in sa; + int one = 1; + s = socket(AF_INET, SOCK_STREAM, 0); - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - memset(&sa, 0, sizeof(sa)); - sa.sin_port = htons(21); - sa.sin_addr.s_addr = INADDR_ANY; - bind(s, (struct sockaddr*)&sa, sizeof(sa)); - listen(s, 5); - t = accept(s, NULL, 0); - close(s); - dup2(t, 0); - dup2(t, 1); - if(t > 2) - close(t); + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + memset(&sa, 0, sizeof(sa)); + sa.sin_port = htons(21); + sa.sin_addr.s_addr = INADDR_ANY; + bind(s, (struct sockaddr*)&sa, sizeof(sa)); + listen(s, 5); + t = accept(s, NULL, 0); + close(s); + dup2(t, 0); + dup2(t, 1); + if(t > 2) + close(t); } @@ -258,10 +258,16 @@ main(int argc, char **argv, char **envp) char *cp, line[LINE_MAX]; FILE *fd; + char tkfile[1024]; + #if 0 conn_wait(); #endif + sprintf(tkfile, "/tmp/ftp_%d", getpid()); + setenv("KRBTKFILE", tkfile); + if(k_hasafs()) + k_setpag(); /* * LOG_NDELAY sets up the logging connection immediately, * necessary for anonymous ftp's that chroot and can't do it later. @@ -988,6 +994,8 @@ send_data(FILE *instr, FILE *outstr, off_t blksize) { int c, cnt, filefd, netfd; char *buf; + int i = 0; + char s[1024]; transflag++; if (setjmp(urgcatch)) { @@ -998,14 +1006,18 @@ send_data(FILE *instr, FILE *outstr, off_t blksize) case TYPE_A: while ((c = getc(instr)) != EOF) { - byte_count++; - if (c == '\n') { - if (ferror(outstr)) - goto data_err; - (void) putc('\r', outstr); - } - (void) putc(c, outstr); + byte_count++; + if(i > 1022){ + auth_write(fileno(outstr), s, i); + i = 0; + } + if(c == '\n') + s[i++] = '\r'; + s[i++] = c; } + if(i) + auth_write(fileno(outstr), s, i); + auth_write(fileno(outstr), s, 0); fflush(outstr); transflag = 0; if (ferror(instr)) @@ -1025,8 +1037,9 @@ send_data(FILE *instr, FILE *outstr, off_t blksize) netfd = fileno(outstr); filefd = fileno(instr); while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && - write(netfd, buf, cnt) == cnt) - byte_count += cnt; + auth_write(netfd, buf, cnt) == cnt) + byte_count += cnt; + auth_write(netfd, buf, 0); /* to end an encrypted stream */ transflag = 0; (void)free(buf); if (cnt != 0) { @@ -1061,79 +1074,90 @@ file_err: static int receive_data(FILE *instr, FILE *outstr) { - int c; - int cnt, bare_lfs = 0; - char buf[BUFSIZ]; + int c; + int cnt, bare_lfs = 0; + char buf[BUFSIZ]; - transflag++; - if (setjmp(urgcatch)) { - transflag = 0; - return (-1); + transflag++; + if (setjmp(urgcatch)) { + transflag = 0; + return (-1); + } + switch (type) { + + case TYPE_I: + case TYPE_L: + while ((cnt = auth_read(fileno(instr), buf, sizeof(buf))) > 0) { + if (write(fileno(outstr), buf, cnt) != cnt) + goto file_err; + byte_count += cnt; } - switch (type) { + if (cnt < 0) + goto data_err; + transflag = 0; + return (0); - case TYPE_I: - case TYPE_L: - while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { - if (write(fileno(outstr), buf, cnt) != cnt) - goto file_err; - byte_count += cnt; - } - if (cnt < 0) - goto data_err; - transflag = 0; - return (0); + case TYPE_E: + reply(553, "TYPE E not implemented."); + transflag = 0; + return (-1); - case TYPE_E: - reply(553, "TYPE E not implemented."); - transflag = 0; - return (-1); - - case TYPE_A: - while ((c = getc(instr)) != EOF) { - byte_count++; - if (c == '\n') - bare_lfs++; - while (c == '\r') { - if (ferror(outstr)) - goto data_err; - if ((c = getc(instr)) != '\n') { - (void) putc ('\r', outstr); - if (c == '\0' || c == EOF) - goto contin2; - } - } - (void) putc(c, outstr); - contin2: ; - } - fflush(outstr); - if (ferror(instr)) - goto data_err; - if (ferror(outstr)) - goto file_err; - transflag = 0; - if (bare_lfs) { - lreply(226, - "WARNING! %d bare linefeeds received in ASCII mode", - bare_lfs); - (void)printf(" File may not have transferred correctly.\r\n"); - } - return (0); - default: - reply(550, "Unimplemented TYPE %d in receive_data", type); - transflag = 0; - return (-1); + case TYPE_A: + { + char *p, *q; + int cr_flag = 0; + while ((cnt = auth_read(fileno(instr), buf+cr_flag, sizeof(buf))) > 0){ + byte_count += cnt; + cr_flag = 0; + for(p = buf, q = buf; p < buf + cnt;){ + if(*p == '\n') + bare_lfs++; + if(*p == '\r') + if(p == buf + cnt - 1){ + cr_flag = 1; + p++; + continue; + }else if(p[1] == '\n'){ + *q++ = '\n'; + p += 2; + continue; + } + *q++ = *p++; + } + fwrite(buf, q - buf, 1, outstr); + if(cr_flag) + buf[0] = '\r'; } - + if(cr_flag) + putc('\r', outstr); + fflush(outstr); + if (ferror(instr)) + goto data_err; + if (ferror(outstr)) + goto file_err; + transflag = 0; + if (bare_lfs) { + lreply(226, "WARNING! %d bare linefeeds received in ASCII mode\r\n" + " File may not have transferred correctly.\r\n", + bare_lfs); + } + return (0); + } + default: + reply(550, "Unimplemented TYPE %d in receive_data", type); + transflag = 0; + return (-1); + } + data_err: - transflag = 0; - perror_reply(426, "Data Connection"); - return (-1); - + transflag = 0; + perror_reply(426, "Data Connection"); + return (-1); + file_err: - transflag = 0; - perror_reply(452, "Error writing file"); - return (-1); + transflag = 0; + perror_reply(452, "Error writing file"); + return (-1); } void @@ -1417,10 +1441,9 @@ dologout(int status) if (logged_in) { (void) seteuid((uid_t)0); logwtmp(ttyline, "", ""); -#if defined(KERBEROS) - if (!notickets && krbtkfile_env) - unlink(krbtkfile_env); -#endif + dest_tkt(); + if(k_hasafs()) + k_unlog(); } /* beware of flushing buffers after a SIGPIPE */ _exit(status); diff --git a/appl/ftp/ftpd/kauth.c b/appl/ftp/ftpd/kauth.c new file mode 100644 index 000000000..6e07d6f28 --- /dev/null +++ b/appl/ftp/ftpd/kauth.c @@ -0,0 +1,175 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include + +#include +#include +#include + +#include +#include +#include + +#include "extern.h" +#include "krb4.h" +#include "auth.h" + + +static KTEXT_ST cip; +static time_t local_time; + +static char name[ANAME_SZ], inst[INST_SZ], realm[REALM_SZ]; + +static int +save_tkt(char *user, char *instance, char *realm, void *arg, + key_proc_t key_proc, KTEXT *cipp) +{ + local_time = time(0); + memmove(&cip, *cipp, sizeof(cip)); + return -1; +} + +static int +store_ticket(KTEXT cip) +{ + char *ptr; + des_cblock session; + char sname[SNAME_SZ]; + char sinst[INST_SZ]; + char srealm[REALM_SZ]; + unsigned char lifetime; + unsigned char kvno; + KTEXT_ST tkt; + + int kerror; + + time_t kdc_time; + + ptr = (char *) cip->dat; + + /* extract session key */ + memmove(session, ptr, 8); + ptr += 8; + + if ((strlen(ptr) + (ptr - (char *) cip->dat)) > cip->length) + return(INTK_BADPW); + + /* extract server's name */ + strcpy(sname,ptr); + ptr += strlen(sname) + 1; + + if ((strlen(ptr) + (ptr - (char *) cip->dat)) > cip->length) + return(INTK_BADPW); + + /* extract server's instance */ + strcpy(sinst, ptr); + ptr += strlen(sinst) + 1; + + if ((strlen(ptr) + (ptr - (char *) cip->dat)) > cip->length) + return(INTK_BADPW); + + /* extract server's realm */ + strcpy(srealm,ptr); + ptr += strlen(srealm) + 1; + + /* extract ticket lifetime, server key version, ticket length */ + /* be sure to avoid sign extension on lifetime! */ + lifetime = (unsigned char) ptr[0]; + kvno = (unsigned char) ptr[1]; + tkt.length = (unsigned char) ptr[2]; + ptr += 3; + + if ((tkt.length < 0) || + ((tkt.length + (ptr - (char *) cip->dat)) > cip->length)) + return(INTK_BADPW); + + /* extract ticket itself */ + memmove(tkt.dat, ptr, tkt.length); + ptr += tkt.length; + + /* Here is where the time should be verified against the KDC. + * Unfortunately everything is sent in host byte order (receiver + * makes wrong) , and at this stage there is no way for us to know + * which byteorder the KDC has. So we simply ignore the time, + * there are no security risks with this, the only thing that can + * happen is that we might receive a replayed ticket, which could + * at most be useless. + */ + +#if 0 + /* check KDC time stamp */ + memmove(&kdc_time, ptr, sizeof(kdc_time)); + if (swap_bytes) swap_u_long(kdc_time); + + ptr += 4; + + if (abs((int)(local_time - kdc_time)) > CLOCK_SKEW) { + return(RD_AP_TIME); /* XXX should probably be better + code */ + } +#endif + + /* initialize ticket cache */ + + if (tf_create(TKT_FILE) != KSUCCESS) + return(INTK_ERR); + + if (tf_put_pname(name) != KSUCCESS || + tf_put_pinst(inst) != KSUCCESS) { + tf_close(); + return(INTK_ERR); + } + + + kerror = tf_save_cred(sname, sinst, srealm, session, lifetime, kvno, + &tkt, local_time); + tf_close(); + + return(kerror); +} + +void kauth(char *principal, char *ticket) +{ + char *p; + int ret; + + ret = kname_parse(name, inst, realm, principal); + if(ret){ + reply(500, "Bad principal: %s.", krb_get_err_text(ret)); + return; + } + if(realm[0] == 0) + krb_get_lrealm(realm, 0); + + if(ticket){ + cip.length = base64_decode(ticket, &cip.dat); + if(cip.length == -1){ + reply(500, "Failed to decode data."); + return; + } + ret = store_ticket(&cip); + if(ret){ + reply(500, "Kerberos error: %s.", krb_get_err_text(ret)); + memset(&cip, 0, sizeof(cip)); + return; + } + if(k_hasafs()) + k_afsklog(0, 0); + reply(200, "OK"); + return; + } + + ret = krb_get_in_tkt (name, inst, realm, "krbtgt", realm, 12, + NULL, save_tkt, NULL); + if(ret != INTK_BADPW){ + reply(500, "Kerberos error: %s.", krb_get_err_text(ret)); + return; + } + base64_encode(cip.dat, cip.length, &p); + reply(300, "P=%s%s%s@%s T=%s", name, *inst?".":"", inst, realm, p); + free(p); + memset(&cip, 0, sizeof(cip)); +} diff --git a/appl/ftp/ftpd/krb4.c b/appl/ftp/ftpd/krb4.c index ebafbb59e..a49d97ae7 100644 --- a/appl/ftp/ftpd/krb4.c +++ b/appl/ftp/ftpd/krb4.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -20,176 +21,292 @@ static des_key_schedule schedule; int krb4_auth(char *auth) { - auth_complete = 0; - reply(334, "Using authentication type %s; ADAT must follow", auth); - return 0; + auth_complete = 0; + reply(334, "Using authentication type %s; ADAT must follow", auth); + return 0; } int krb4_adat(char *auth) { - KTEXT_ST tkt; - char *p; - int kerror; - u_int32_t cs; - char msg[35]; /* size of encrypted block */ - int len; + KTEXT_ST tkt; + char *p; + int kerror; + u_int32_t cs; + char msg[35]; /* size of encrypted block */ + int len; - char inst[INST_SZ]; + char inst[INST_SZ]; - memset(&tkt, 0, sizeof(tkt)); - tkt.length = base64_decode(auth, tkt.dat); + memset(&tkt, 0, sizeof(tkt)); + tkt.length = base64_decode(auth, tkt.dat); - strcpy(inst, "*"); - kerror = krb_rd_req(&tkt, "ftp", inst, 0, &auth_dat, ""); - if(kerror == RD_AP_UNDEC){ - strcpy(inst, "*"); - kerror = krb_rd_req(&tkt, "rcmd", inst, 0, &auth_dat, ""); - } + if(tkt.length < 0){ + reply(501, "Failed to decode base64 data."); + return -1; + } + + k_getsockinst(0, inst); + kerror = krb_rd_req(&tkt, "ftp", inst, 0, &auth_dat, ""); + if(kerror == RD_AP_UNDEC){ + k_getsockinst(0, inst); + kerror = krb_rd_req(&tkt, "rcmd", inst, 0, &auth_dat, ""); + } + + if(kerror){ + reply(535, "Error reading request: %s.", krb_get_err_text(kerror)); + return -1; + } - des_key_sched(&auth_dat.session, schedule); + des_set_key(&auth_dat.session, schedule); - if(kerror != RD_AP_OK){ - reply(535, "%s", krb_err_txt[kerror]); - return 1; - } - - cs = htonl(auth_dat.checksum + 1); - len = krb_mk_safe((u_char*)&cs, (u_char*)msg, sizeof(cs), - &auth_dat.session, &ctrl_addr, &his_addr); - base64_encode((unsigned char*)msg, len, &p); - reply(235, "ADAT=%s", p); - auth_complete = 1; - free(p); - return 0; + cs = htonl(auth_dat.checksum + 1); + len = krb_mk_safe(&cs, msg, sizeof(cs), &auth_dat.session, + &ctrl_addr, &his_addr); + if(len < 0){ + reply(535, "Error creating reply: %s.", k_strerror(errno)); + return -1; + } + base64_encode(msg, len, &p); + reply(235, "ADAT=%s", p); + auth_complete = 1; + free(p); + return 0; } int krb4_pbsz(int size) { - buffer_size = size; - reply(200, "OK"); - return 0; + if(size > 1048576) /* XXX arbitrary number */ + size = 1048576; + buffer_size = size; + reply(200, "OK PBSZ=%d", buffer_size); + return 0; } -int krb4_prot(char *type) +int krb4_prot(int level) { - if(!strcmp(type, "C")){ - data_protection = prot_clear; - }else if(!strcmp(type, "S")){ - data_protection = prot_safe; - }else if(!strcmp(type, "E")){ - data_protection = prot_confidential; - }else if(!strcmp(type, "P")){ - data_protection = prot_private; - }else{ - reply(504, "Unrecognized protection level"); - return 1; - } - reply(200, "OK"); - return 0; + if(level == prot_confidential) + return -1; + return 0; } int krb4_ccc(void) { - reply(500, "Gurka"); - return 1; + reply(534, "Don't event think about it."); + return -1; } int krb4_mic(char *msg) { - char *cmd = (char*)malloc(strlen(msg)); - int len; - int kerror; - MSG_DAT m_data; - char *p; - char tmp[1024]; - unsigned char enc[1024]; + int len; + int kerror; + MSG_DAT m_data; + char *p; + char *tmp, *cmd; - len = base64_decode(msg, cmd); - kerror = krb_rd_safe(cmd, len, &auth_dat.session, - &ctrl_addr, &his_addr, &m_data); - sprintf(tmp, "%.*s\r\n", m_data.app_length, m_data.app_data); - new_ftp_command(strdup(tmp)); - free(cmd); - return 0; + cmd = strdup(msg); + + len = base64_decode(msg, cmd); + if(len < 0){ + reply(501, "Failed to decode base 64 data."); + free(cmd); + return -1; + } + kerror = krb_rd_safe(cmd, len, &auth_dat.session, + &his_addr, &ctrl_addr, &m_data); + + if(kerror){ + reply(535, "Error reading request: %s.", krb_get_err_text(kerror)); + free(cmd); + return -1; + } + + tmp = strdup(msg); + sprintf(tmp, "%.*s", m_data.app_length, m_data.app_data); + if(!strstr(tmp, "\r\n")) + strcat(tmp, "\r\n"); + new_ftp_command(tmp); + free(cmd); + return 0; } int krb4_conf(char *msg) { - char tmp[1024]; - unsigned char enc[1024]; - int len; - char *p; - - sprintf(tmp, "%d %s\r\n", 536, - "Requested PROT level not supported by mechanism"); - len = krb_mk_safe((u_char*)tmp, (u_char*)enc, strlen(tmp), &auth_dat.session, - &ctrl_addr, &his_addr); - if(len > 0){ - base64_encode(enc, len, &p); - fprintf(stdout, "631 %s\r\n", p); - free(p); - } - return 1; + prot_level = prot_safe; + + reply(537, "Protection level not supported."); + return -1; } int krb4_enc(char *msg) { - char *cmd = (char*)malloc(strlen(msg)); - int len; - int kerror; - MSG_DAT m_data; - char *p; - - char tmp[1024]; - unsigned char enc[1024]; + int len; + int kerror; + MSG_DAT m_data; + char *p; + char *tmp, *cmd; - len = base64_decode(msg, cmd); - - kerror = krb_rd_priv(cmd, len, schedule, &auth_dat.session, - &ctrl_addr, &his_addr, &m_data); - sprintf(tmp, "%.*s\r\n", m_data.app_length, m_data.app_data); - new_ftp_command(strdup(tmp)); + cmd = strdup(msg); + + len = base64_decode(msg, cmd); + if(len < 0){ + reply(501, "Failed to decode base 64 data."); + free(cmd); + return -1; + } + kerror = krb_rd_priv(cmd, len, schedule, &auth_dat.session, + &his_addr, &ctrl_addr, &m_data); - free(cmd); - return 0; + if(kerror){ + reply(535, "Error reading request: %s.", krb_get_err_text(kerror)); + free(cmd); + return -1; + } + + tmp = strdup(msg); + sprintf(tmp, "%.*s", m_data.app_length, m_data.app_data); + if(!strstr(tmp, "\r\n")) + strcat(tmp, "\r\n"); + new_ftp_command(tmp); + free(cmd); + return 0; +} + +int krb4_read(int fd, void *data, int length) +{ + static int left; + static char *extra; + static int eof; + int len, bytes, tx = 0; + + MSG_DAT m_data; + int kerror; + + if(eof){ /* if we haven't reported an end-of-file, do so */ + eof = 0; + return 0; + } + + if(left){ + if(length > left) + bytes = left; + else + bytes = length; + memmove(data, extra, bytes); + left -= bytes; + if(left) + memmove(extra, extra + bytes, left); + else + free(extra); + length -= bytes; + tx += bytes; + } + + while(length){ + krb_net_read(fd, &len, 4); + len = ntohl(len); + krb_net_read(fd, data_buffer, len); + if(data_protection == prot_safe) + kerror = krb_rd_safe(data_buffer, len, &auth_dat.session, + &his_addr, &ctrl_addr, &m_data); + else + kerror = krb_rd_priv(data_buffer, len, schedule, &auth_dat.session, + &his_addr, &ctrl_addr, &m_data); + + if(kerror){ + reply(400, "Failed to read data: %s.", krb_get_err_text(kerror)); + return -1; + } + + bytes = m_data.app_length; + if(bytes == 0){ + if(tx) eof = 1; + return tx; + } + if(bytes > length){ + left = bytes - length; + bytes = length; + extra = malloc(left); + memmove(extra, m_data.app_data + bytes, left); + } + memmove((unsigned char*)data + tx, m_data.app_data, bytes); + tx += bytes; + length -= bytes; + } + return tx; +} + +int krb4_write(int fd, void *data, int length) +{ + int len, bytes, tx = 0; + + len = buffer_size; + if(data_protection == prot_safe) + len -= 31; /* always 31 bytes overhead */ + else + len -= 26; /* at most 26 bytes */ + + do{ + if(length < len) + len = length; + if(data_protection == prot_safe) + bytes = krb_mk_safe(data, data_buffer+4, len, &auth_dat.session, + &ctrl_addr, &his_addr); + else + bytes = krb_mk_priv(data, data_buffer+4, len, schedule, + &auth_dat.session, + &ctrl_addr, &his_addr); + if(bytes == -1){ + reply(535, "Failed to make packet: %s.", k_strerror(errno)); + return -1; + } + data_buffer[0] = (bytes >> 24) & 0xff; + data_buffer[1] = (bytes >> 16) & 0xff; + data_buffer[2] = (bytes >> 8) & 0xff; + data_buffer[3] = bytes & 0xff; + krb_net_write(fd, data_buffer, bytes+4); + length -= len; + data = (unsigned char*)data + len; + tx += len; + }while(length); + return tx; } int krb4_userok(char *name) { - if(!kuserok(&auth_dat, name)){ - do_login(232, name); - }else{ - reply(530, "User %s access denied.", name); - } - return 0; + if(!kuserok(&auth_dat, name)){ + do_login(232, name); + }else{ + reply(530, "User %s access denied.", name); + } + return 0; } int krb4_vprintf(const char *fmt, va_list ap) { - char buf[10240]; - char *p; - char *enc; - int code; - int len; + char buf[10240]; /* XXX */ + char *p; + char *enc; + int code; + int len; - vsprintf(buf, fmt, ap); - enc = malloc(strlen(buf) + 31); - if(prot_level == prot_safe){ - len = krb_mk_safe((u_char*)buf, (u_char*)enc, strlen(buf), &auth_dat.session, - &ctrl_addr, &his_addr); - code = 631; - }else if(prot_level == prot_private){ - len = krb_mk_priv((u_char*)buf, (u_char*)enc, strlen(buf), schedule, - &auth_dat.session, &ctrl_addr, &his_addr); - code = 632; - }else{ - len = 0; /* XXX */ - code = 631; - } - base64_encode(enc, len, &p); - fprintf(stdout, "%d %s\r\n", code, p); - free(enc); - free(p); - return 0; + vsprintf(buf, fmt, ap); + enc = malloc(strlen(buf) + 31); + if(prot_level == prot_safe){ + len = krb_mk_safe((u_char*)buf, (u_char*)enc, strlen(buf), &auth_dat.session, + &ctrl_addr, &his_addr); + code = 631; + }else if(prot_level == prot_private){ + len = krb_mk_priv((u_char*)buf, (u_char*)enc, strlen(buf), schedule, + &auth_dat.session, &ctrl_addr, &his_addr); + code = 632; + }else{ + len = 0; /* XXX */ + code = 631; + } + base64_encode(enc, len, &p); + fprintf(stdout, "%d %s\r\n", code, p); + free(enc); + free(p); + return 0; } diff --git a/appl/ftp/ftpd/krb4.h b/appl/ftp/ftpd/krb4.h index 3aaea8c65..bff19abef 100644 --- a/appl/ftp/ftpd/krb4.h +++ b/appl/ftp/ftpd/krb4.h @@ -1,17 +1,21 @@ -#ifndef _KRB4_H_ -#define _KRB4_H_ +#ifndef __KRB4_H__ +#define __KRB4_H__ #include -extern int krb4_auth(char * auth); -extern int krb4_adat(char * auth); -extern int krb4_pbsz(int size); -extern int krb4_prot(char * type); -extern int krb4_ccc(void ); -extern int krb4_mic(char * msg); -extern int krb4_conf(char * msg); -extern int krb4_enc(char * msg); -extern int krb4_userok(char *name); -extern int krb4_vprintf(const char *fmt, va_list ap); +int krb4_auth(char *auth); +int krb4_adat(char *auth); +int krb4_pbsz(int size); +int krb4_prot(int level); +int krb4_ccc(void); +int krb4_mic(char *msg); +int krb4_conf(char *msg); +int krb4_enc(char *msg); -#endif /* _KRB4_H_ */ +int krb4_read(int fd, void *data, int length); +int krb4_write(int fd, void *data, int length); + +int krb4_userok(char *name); +int krb4_vprintf(const char *fmt, va_list ap); + +#endif /* __KRB4_H__ */