aboutsummaryrefslogtreecommitdiff
path: root/src/appl/gssftp/ftpd/ftpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/appl/gssftp/ftpd/ftpd.c')
-rw-r--r--src/appl/gssftp/ftpd/ftpd.c198
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);
/*