diff options
Diffstat (limited to 'src/appl/gssftp/ftpd/ftpd.c')
-rw-r--r-- | src/appl/gssftp/ftpd/ftpd.c | 198 |
1 files changed, 157 insertions, 41 deletions
diff --git a/src/appl/gssftp/ftpd/ftpd.c b/src/appl/gssftp/ftpd/ftpd.c index c7dfc8a..d4ded83 100644 --- a/src/appl/gssftp/ftpd/ftpd.c +++ b/src/appl/gssftp/ftpd/ftpd.c @@ -109,10 +109,12 @@ static char sccsid[] = "@(#)ftpd.c 5.40 (Berkeley) 7/2/91"; #define L_INCR 1 #endif +#ifndef HAVE_STRERROR #define strerror(error) (sys_errlist[error]) #ifdef NEED_SYS_ERRLIST extern char *sys_errlist[]; #endif +#endif extern char *mktemp (); @@ -761,7 +763,17 @@ user(name) int result; #ifdef GSSAPI if (auth_type && strcmp(auth_type, "GSSAPI") == 0) { + int len; + authorized = ftpd_gss_userok(&client_name, name) == 0; + len = sizeof("GSSAPI user is not authorized as " + "; Password required.") + + strlen(client_name.value) + + strlen(name); + if (len >= sizeof(buf)) { + syslog(LOG_ERR, "user: username too long"); + name = "[username too long]"; + } sprintf(buf, "GSSAPI user %s is%s authorized as %s", client_name.value, authorized ? "" : " not", name); @@ -772,7 +784,19 @@ user(name) #endif /* GSSAPI */ #ifdef KRB5_KRB4_COMPAT if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) { + int len; + authorized = kuserok(&kdata,name) == 0; + len = sizeof("Kerberos user .@ is not authorized as " + "; Password required.") + + strlen(kdata.pname) + + strlen(kdata.pinst) + + strlen(kdata.prealm) + + strlen(name); + if (len >= sizeof(buf)) { + syslog(LOG_ERR, "user: username too long"); + name = "[username too long]"; + } sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s", kdata.pname, *kdata.pinst ? "." : "", kdata.pinst, kdata.prealm, @@ -796,7 +820,7 @@ user(name) syslog(authorized ? LOG_INFO : LOG_ERR, "%s", buf); if (result == 232) - login(NULL); + login(NULL, result); return; } @@ -1064,11 +1088,11 @@ pass(passwd) } login_attempts = 0; /* this time successful */ - login(passwd); + login(passwd, 0); return; } -login(passwd) +login(passwd, logincode) char *passwd; { if (have_creds) { @@ -1127,8 +1151,11 @@ login(passwd) reply(530, "User %s: can't change directory to %s.", pw->pw_name, pw->pw_dir); goto bad; - } else - lreply(230, "No directory! Logging in with home=/"); + } else { + if (!logincode) + logincode = 230; + lreply(logincode, "No directory! Logging in with home=/"); + } } } if (guest) { @@ -1179,6 +1206,11 @@ retrieve(cmd, name) } else { char line[FTP_BUFSIZ]; + if (strlen(cmd) + strlen(name) + 1 >= sizeof(line)) { + syslog(LOG_ERR, "retrieve: filename too long"); + reply(501, "filename too long"); + return; + } (void) sprintf(line, cmd, name), name = line; fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; st.st_size = -1; @@ -1417,6 +1449,10 @@ dataconn(name, size, mode) return (file); } +/* + * XXX callers need to limit total length of output string to + * FTP_BUFSIZ + */ #ifdef STDARG secure_error(char *fmt, ...) #else @@ -1616,13 +1652,19 @@ statfilecmd(filename) { char line[FTP_BUFSIZ]; FILE *fin; - int c; + int c, n; char str[FTP_BUFSIZ], *p; + if (strlen(filename) + sizeof("/bin/ls -lgA ") + >= sizeof(line)) { + reply(501, "filename too long"); + return; + } (void) sprintf(line, "/bin/ls -lgA %s", filename); fin = ftpd_popen(line, "r"); lreply(211, "status of %s:", filename); p = str; + n = 0; while ((c = getc(fin)) != EOF) { if (c == '\n') { if (ferror(stdout)){ @@ -1639,7 +1681,16 @@ statfilecmd(filename) *p = '\0'; reply(0, "%s", str); p = str; - } else *p++ = c; + n = 0; + } else { + *p++ = c; + n++; + if (n >= sizeof(str)) { + reply(551, "output line too long"); + (void) ftpd_pclose(fin); + return; + } + } } if (p != str) { *p = '\0'; @@ -1723,6 +1774,10 @@ fatal(s) char cont_char = ' '; +/* + * XXX callers need to limit total length of output string to + * FTP_BUFSIZ bytes for now. + */ #ifdef STDARG reply(int n, char *fmt, ...) #else @@ -1744,22 +1799,32 @@ reply(n, fmt, p0, p1, p2, p3, p4, p5) #endif if (auth_type) { - char in[FTP_BUFSIZ], out[FTP_BUFSIZ]; + /* + * Deal with expansion in mk_{safe,priv}, + * radix_encode, gss_seal, plus slop. + */ + char in[FTP_BUFSIZ*3/2], out[FTP_BUFSIZ*3/2]; int length, kerror; if (n) sprintf(in, "%d%c", n, cont_char); else in[0] = '\0'; strncat(in, buf, sizeof (in) - strlen(in) - 1); #ifdef KRB5_KRB4_COMPAT if (strcmp(auth_type, "KERBEROS_V4") == 0) { - if ((length = clevel == PROT_P ? - krb_mk_priv((unsigned char *)in, - (unsigned char *)out, - strlen(in), schedule, &kdata.session, - &ctrl_addr, &his_addr) - : krb_mk_safe((unsigned char *)in, - (unsigned char *)out, - strlen(in), &kdata.session, - &ctrl_addr, &his_addr)) == -1) { + if (clevel == PROT_P) + length = krb_mk_priv((unsigned char *)in, + (unsigned char *)out, + strlen(in), + schedule, &kdata.session, + &ctrl_addr, + &his_addr); + else + length = krb_mk_safe((unsigned char *)in, + (unsigned char *)out, + strlen(in), + &kdata.session, + &ctrl_addr, + &his_addr); + if (length == -1) { syslog(LOG_ERR, "krb_mk_%s failed for KERBEROS_V4", clevel == PROT_P ? "priv" : "safe"); @@ -1803,13 +1868,16 @@ reply(n, fmt, p0, p1, p2, p3, p4, p5) } #endif /* GSSAPI */ /* Other auth types go here ... */ - if (kerror = radix_encode(out, in, &length, 0)) { + if (length >= sizeof(in) / 4 * 3) { + syslog(LOG_ERR, "input to radix_encode too long"); + fputs(in, stdout); + } else if (kerror = radix_encode(out, in, &length, 0)) { syslog(LOG_ERR, "Couldn't encode reply (%s)", radix_error(kerror)); fputs(in,stdout); } else - printf("%s%c%s", clevel == PROT_P ? "632" : "631", - n ? cont_char : '-', in); + printf("%s%c%s", clevel == PROT_P ? "632" : "631", + n ? cont_char : '-', in); } else { if (n) printf("%d%c", n, cont_char); fputs(buf, stdout); @@ -1822,6 +1890,10 @@ reply(n, fmt, p0, p1, p2, p3, p4, p5) } } +/* + * XXX callers need to limit total length of output string to + * FTP_BUFSIZ + */ #ifdef STDARG lreply(int n, char *fmt, ...) #else @@ -1866,7 +1938,8 @@ yyerror(s) if (cp = strchr(cbuf,'\n')) *cp = '\0'; - reply(500, "'%s': command not understood.", cbuf); + reply(500, "'%.*s': command not understood.", + FTP_BUFSIZ - sizeof("'': command not understood."), cbuf); } delete_file(name) @@ -2123,7 +2196,8 @@ gunique(local) } if (cp) *cp = '/'; - (void) strcpy(new, local); + (void) strncpy(new, local, sizeof(new) - 1); + new[sizeof(new) - 1] = '\0'; cp = new + strlen(new); *cp++ = '.'; for (count = 1; count < 100; count++) { @@ -2142,7 +2216,23 @@ perror_reply(code, string) int code; char *string; { - reply(code, "%s: %s.", string, strerror(errno)); + char *err_string; + size_t extra_len; + + err_string = strerror(errno); + if (err_string == NULL) + err_string = "(unknown error)"; + extra_len = strlen(err_string) + sizeof("(truncated): ."); + + /* + * XXX knows about FTP_BUFSIZ in reply() + */ + if (strlen(string) + extra_len > FTP_BUFSIZ) { + reply(code, "(truncated)%.*s: %s.", + FTP_BUFSIZ - extra_len, string, err_string); + } else { + reply(code, "%s: %s.", string, err_string); + } } auth(type) @@ -2173,7 +2263,7 @@ char *data; int kerror, length; #ifdef KRB5_KRB4_COMPAT int i; - static char *service; + static char **service=NULL; char instance[INST_SZ]; u_long cksum; char buf[FTP_BUFSIZ]; @@ -2199,23 +2289,22 @@ char *data; } (void) memcpy((char *)ticket.dat, (char *)out_buf, ticket.length = length); strcpy(instance, "*"); - if (!service) { - char realm[REALM_SZ]; - des_cblock key; - - service = "ftp"; - if (krb_get_lrealm(realm, 1) == KSUCCESS && - read_service_key(service, instance, realm, 0, keyfile, key)) - service = "rcmd"; - else - (void) memset(key, 0, sizeof(key)); - } - if (kerror = krb_rd_req(&ticket, service, instance, - his_addr.sin_addr.s_addr, &kdata, keyfile)) { - secure_error("ADAT: Kerberos V4 krb_rd_req: %s", - krb_get_err_text(kerror)); - return(0); + + kerror = 255; + for (service = krb4_services; *service; service++) { + kerror = krb_rd_req(&ticket, *service, instance, + his_addr.sin_addr.s_addr, + &kdata, keyfile); + /* Success */ + if(!kerror) break; + } + /* rd_req failed.... */ + if(kerror) { + secure_error("ADAT: Kerberos V4 krb_rd_req: %s", + krb_get_err_text(kerror)); + return(0); } + /* add one to the (formerly) sealed checksum, and re-seal it */ cksum = kdata.checksum + 1; cksum = htonl(cksum); @@ -2225,6 +2314,10 @@ char *data; secure_error("ADAT: krb_mk_safe failed"); return(0); } + if (length >= (FTP_BUFSIZ - sizeof("ADAT=")) / 4 * 3) { + secure_error("ADAT: reply too long"); + return(0); + } if (kerror = radix_encode(out_buf, buf, &length, 0)) { secure_error("Couldn't encode ADAT reply (%s)", radix_error(kerror)); @@ -2287,7 +2380,8 @@ char *data; syslog(LOG_ERR, "Couldn't canonicalize local hostname"); return 0; } - strcpy(localname, hp->h_name); + strncpy(localname, hp->h_name, sizeof(localname) - 1); + localname[sizeof(localname) - 1] = '\0'; for (service = gss_services; *service; service++) { sprintf(service_name, "%s@%s", *service, localname); @@ -2358,6 +2452,16 @@ char *data; } if (out_tok.length) { + if (out_tok.length >= ((FTP_BUFSIZ - sizeof("ADAT=")) + / 4 * 3)) { + secure_error("ADAT: reply too long"); + syslog(LOG_ERR, "ADAT: reply too long"); + (void) gss_release_cred(&stat_min, &server_creds); + if (ret_flags & GSS_C_DELEG_FLAG) + (void) gss_release_cred(&stat_min, + &deleg_creds); + return(0); + } if (kerror = radix_encode(out_tok.value, gbuf, &out_tok.length, 0)) { secure_error("Couldn't encode ADAT reply (%s)", radix_error(kerror)); @@ -2456,6 +2560,9 @@ static char *onefile[] = { * n>=0 on success * -1 on error * -2 on security error + * + * XXX callers need to limit total length of output string to + * FTP_BUFSIZ */ #ifdef STDARG secure_fprintf(FILE *stream, char *fmt, ...) @@ -2573,6 +2680,15 @@ send_file_list(whichfiles) dir->d_name[2] == '\0') continue; + if (strlen(dirname) + strlen(dir->d_name) + + 1 /* slash */ + + 2 /* CRLF */ + + 1 > sizeof(nbuf)) { + syslog(LOG_ERR, + "send_file_list: pathname too long"); + ret = -2; /* XXX */ + goto data_err; + } sprintf(nbuf, "%s/%s", dirname, dir->d_name); /* |