1/* $NetBSD: conf.c,v 1.52 2004-08-09 12:56:47 lukem Exp $ */
| 1/* $NetBSD: conf.c,v 1.57 2006/02/01 14:20:12 christos Exp $ */
|
2 3/*-
| 2 3/*-
|
4 * Copyright (c) 1997-2004 The NetBSD Foundation, Inc.
| 4 * Copyright (c) 1997-2005 The NetBSD Foundation, Inc.
|
5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Simon Burge and Luke Mewburn. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39#include <sys/cdefs.h> 40#ifndef lint
| 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Simon Burge and Luke Mewburn. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39#include <sys/cdefs.h> 40#ifndef lint
|
41__RCSID("$NetBSD: conf.c,v 1.52 2004-08-09 12:56:47 lukem Exp $");
| 41__RCSID("$NetBSD: conf.c,v 1.57 2006/02/01 14:20:12 christos Exp $");
|
42#endif /* not lint */ 43 44#include <sys/types.h> 45#include <sys/param.h> 46#include <sys/socket.h> 47#include <sys/stat.h> 48 49#include <ctype.h> 50#include <errno.h> 51#include <fcntl.h> 52#include <glob.h> 53#include <netdb.h> 54#include <signal.h> 55#include <stdio.h> 56#include <stdlib.h> 57#include <string.h> 58#include <stringlist.h> 59#include <syslog.h> 60#include <time.h> 61#include <unistd.h> 62#include <util.h> 63 64#ifdef KERBEROS5 65#include <krb5/krb5.h> 66#endif 67 68#include "extern.h" 69#include "pathnames.h" 70 71static char *strend(const char *, char *); 72static int filetypematch(char *, int); 73 74 75 /* class defaults */ 76#define DEFAULT_LIMIT -1 /* unlimited connections */ 77#define DEFAULT_MAXFILESIZE -1 /* unlimited file size */ 78#define DEFAULT_MAXTIMEOUT 7200 /* 2 hours */ 79#define DEFAULT_TIMEOUT 900 /* 15 minutes */
| 42#endif /* not lint */ 43 44#include <sys/types.h> 45#include <sys/param.h> 46#include <sys/socket.h> 47#include <sys/stat.h> 48 49#include <ctype.h> 50#include <errno.h> 51#include <fcntl.h> 52#include <glob.h> 53#include <netdb.h> 54#include <signal.h> 55#include <stdio.h> 56#include <stdlib.h> 57#include <string.h> 58#include <stringlist.h> 59#include <syslog.h> 60#include <time.h> 61#include <unistd.h> 62#include <util.h> 63 64#ifdef KERBEROS5 65#include <krb5/krb5.h> 66#endif 67 68#include "extern.h" 69#include "pathnames.h" 70 71static char *strend(const char *, char *); 72static int filetypematch(char *, int); 73 74 75 /* class defaults */ 76#define DEFAULT_LIMIT -1 /* unlimited connections */ 77#define DEFAULT_MAXFILESIZE -1 /* unlimited file size */ 78#define DEFAULT_MAXTIMEOUT 7200 /* 2 hours */ 79#define DEFAULT_TIMEOUT 900 /* 15 minutes */
|
80#define DEFAULT_UMASK 027 /* 15 minutes */
| 80#define DEFAULT_UMASK 027 /* rw-r----- */
|
81 82/* 83 * Initialise curclass to an `empty' state 84 */ 85void 86init_curclass(void) 87{ 88 struct ftpconv *conv, *cnext; 89 90 for (conv = curclass.conversions; conv != NULL; conv = cnext) { 91 REASSIGN(conv->suffix, NULL); 92 REASSIGN(conv->types, NULL); 93 REASSIGN(conv->disable, NULL); 94 REASSIGN(conv->command, NULL); 95 cnext = conv->next; 96 free(conv); 97 } 98 99 memset((char *)&curclass.advertise, 0, sizeof(curclass.advertise)); 100 curclass.advertise.su_len = 0; /* `not used' */ 101 REASSIGN(curclass.chroot, NULL); 102 REASSIGN(curclass.classname, NULL); 103 curclass.conversions = NULL; 104 REASSIGN(curclass.display, NULL); 105 REASSIGN(curclass.homedir, NULL); 106 curclass.limit = DEFAULT_LIMIT; 107 REASSIGN(curclass.limitfile, NULL); 108 curclass.maxfilesize = DEFAULT_MAXFILESIZE; 109 curclass.maxrateget = 0; 110 curclass.maxrateput = 0; 111 curclass.maxtimeout = DEFAULT_MAXTIMEOUT;
| 81 82/* 83 * Initialise curclass to an `empty' state 84 */ 85void 86init_curclass(void) 87{ 88 struct ftpconv *conv, *cnext; 89 90 for (conv = curclass.conversions; conv != NULL; conv = cnext) { 91 REASSIGN(conv->suffix, NULL); 92 REASSIGN(conv->types, NULL); 93 REASSIGN(conv->disable, NULL); 94 REASSIGN(conv->command, NULL); 95 cnext = conv->next; 96 free(conv); 97 } 98 99 memset((char *)&curclass.advertise, 0, sizeof(curclass.advertise)); 100 curclass.advertise.su_len = 0; /* `not used' */ 101 REASSIGN(curclass.chroot, NULL); 102 REASSIGN(curclass.classname, NULL); 103 curclass.conversions = NULL; 104 REASSIGN(curclass.display, NULL); 105 REASSIGN(curclass.homedir, NULL); 106 curclass.limit = DEFAULT_LIMIT; 107 REASSIGN(curclass.limitfile, NULL); 108 curclass.maxfilesize = DEFAULT_MAXFILESIZE; 109 curclass.maxrateget = 0; 110 curclass.maxrateput = 0; 111 curclass.maxtimeout = DEFAULT_MAXTIMEOUT;
|
112 REASSIGN(curclass.motd, xstrdup(_PATH_FTPLOGINMESG));
| 112 REASSIGN(curclass.motd, ftpd_strdup(_NAME_FTPLOGINMESG));
|
113 REASSIGN(curclass.notify, NULL); 114 curclass.portmin = 0; 115 curclass.portmax = 0; 116 curclass.rateget = 0; 117 curclass.rateput = 0; 118 curclass.timeout = DEFAULT_TIMEOUT; 119 /* curclass.type is set elsewhere */ 120 curclass.umask = DEFAULT_UMASK; 121 curclass.mmapsize = 0; 122 curclass.readsize = 0; 123 curclass.writesize = 0; 124 curclass.sendbufsize = 0; 125 curclass.sendlowat = 0; 126 127 CURCLASS_FLAGS_SET(checkportcmd); 128 CURCLASS_FLAGS_CLR(denyquick);
| 113 REASSIGN(curclass.notify, NULL); 114 curclass.portmin = 0; 115 curclass.portmax = 0; 116 curclass.rateget = 0; 117 curclass.rateput = 0; 118 curclass.timeout = DEFAULT_TIMEOUT; 119 /* curclass.type is set elsewhere */ 120 curclass.umask = DEFAULT_UMASK; 121 curclass.mmapsize = 0; 122 curclass.readsize = 0; 123 curclass.writesize = 0; 124 curclass.sendbufsize = 0; 125 curclass.sendlowat = 0; 126 127 CURCLASS_FLAGS_SET(checkportcmd); 128 CURCLASS_FLAGS_CLR(denyquick);
|
| 129 CURCLASS_FLAGS_CLR(hidesymlinks);
|
129 CURCLASS_FLAGS_SET(modify); 130 CURCLASS_FLAGS_SET(passive); 131 CURCLASS_FLAGS_CLR(private); 132 CURCLASS_FLAGS_CLR(sanenames); 133 CURCLASS_FLAGS_SET(upload); 134} 135 136/* 137 * Parse the configuration file, looking for the named class, and 138 * define curclass to contain the appropriate settings. 139 */ 140void 141parse_conf(const char *findclass) 142{ 143 FILE *f; 144 char *buf, *p; 145 size_t len; 146 LLT llval; 147 int none, match; 148 char *endp, errbuf[100]; 149 char *class, *word, *arg, *template; 150 const char *infile; 151 size_t line; 152 struct ftpconv *conv, *cnext; 153 154 init_curclass();
| 130 CURCLASS_FLAGS_SET(modify); 131 CURCLASS_FLAGS_SET(passive); 132 CURCLASS_FLAGS_CLR(private); 133 CURCLASS_FLAGS_CLR(sanenames); 134 CURCLASS_FLAGS_SET(upload); 135} 136 137/* 138 * Parse the configuration file, looking for the named class, and 139 * define curclass to contain the appropriate settings. 140 */ 141void 142parse_conf(const char *findclass) 143{ 144 FILE *f; 145 char *buf, *p; 146 size_t len; 147 LLT llval; 148 int none, match; 149 char *endp, errbuf[100]; 150 char *class, *word, *arg, *template; 151 const char *infile; 152 size_t line; 153 struct ftpconv *conv, *cnext; 154 155 init_curclass();
|
155 REASSIGN(curclass.classname, xstrdup(findclass));
| 156 REASSIGN(curclass.classname, ftpd_strdup(findclass));
|
156 /* set more guest defaults */ 157 if (strcasecmp(findclass, "guest") == 0) { 158 CURCLASS_FLAGS_CLR(modify); 159 curclass.umask = 0707; 160 } 161
| 157 /* set more guest defaults */ 158 if (strcasecmp(findclass, "guest") == 0) { 159 CURCLASS_FLAGS_CLR(modify); 160 curclass.umask = 0707; 161 } 162
|
162 infile = conffilename(_PATH_FTPDCONF);
| 163 infile = conffilename(_NAME_FTPDCONF);
|
163 if ((f = fopen(infile, "r")) == NULL) 164 return; 165 166 line = 0; 167 template = NULL; 168 for (; 169 (buf = fparseln(f, &len, &line, NULL, FPARSELN_UNESCCOMM | 170 FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) != NULL; 171 free(buf)) { 172 none = match = 0; 173 p = buf; 174 if (len < 1) 175 continue; 176 if (p[len - 1] == '\n') 177 p[--len] = '\0'; 178 if (EMPTYSTR(p)) 179 continue; 180 181 NEXTWORD(p, word); 182 NEXTWORD(p, class); 183 NEXTWORD(p, arg); 184 if (EMPTYSTR(word) || EMPTYSTR(class)) 185 continue; 186 if (strcasecmp(class, "none") == 0) 187 none = 1; 188 if (! (strcasecmp(class, findclass) == 0 || 189 (template != NULL && strcasecmp(class, template) == 0) || 190 none || 191 strcasecmp(class, "all") == 0) ) 192 continue; 193 194#define CONF_FLAG(Field) \ 195 do { \ 196 if (none || \ 197 (!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) \ 198 CURCLASS_FLAGS_CLR(Field); \ 199 else \ 200 CURCLASS_FLAGS_SET(Field); \ 201 } while (0) 202 203#define CONF_STRING(Field) \ 204 do { \ 205 if (none || EMPTYSTR(arg)) \ 206 arg = NULL; \ 207 else \
| 164 if ((f = fopen(infile, "r")) == NULL) 165 return; 166 167 line = 0; 168 template = NULL; 169 for (; 170 (buf = fparseln(f, &len, &line, NULL, FPARSELN_UNESCCOMM | 171 FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) != NULL; 172 free(buf)) { 173 none = match = 0; 174 p = buf; 175 if (len < 1) 176 continue; 177 if (p[len - 1] == '\n') 178 p[--len] = '\0'; 179 if (EMPTYSTR(p)) 180 continue; 181 182 NEXTWORD(p, word); 183 NEXTWORD(p, class); 184 NEXTWORD(p, arg); 185 if (EMPTYSTR(word) || EMPTYSTR(class)) 186 continue; 187 if (strcasecmp(class, "none") == 0) 188 none = 1; 189 if (! (strcasecmp(class, findclass) == 0 || 190 (template != NULL && strcasecmp(class, template) == 0) || 191 none || 192 strcasecmp(class, "all") == 0) ) 193 continue; 194 195#define CONF_FLAG(Field) \ 196 do { \ 197 if (none || \ 198 (!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) \ 199 CURCLASS_FLAGS_CLR(Field); \ 200 else \ 201 CURCLASS_FLAGS_SET(Field); \ 202 } while (0) 203 204#define CONF_STRING(Field) \ 205 do { \ 206 if (none || EMPTYSTR(arg)) \ 207 arg = NULL; \ 208 else \
|
208 arg = xstrdup(arg); \
| 209 arg = ftpd_strdup(arg); \
|
209 REASSIGN(curclass.Field, arg); \ 210 } while (0) 211 212#define CONF_LL(Field,Arg,Min,Max) \ 213 do { \ 214 if (none || EMPTYSTR(Arg)) \ 215 goto nextline; \ 216 llval = strsuftollx(#Field, Arg, Min, Max, \ 217 errbuf, sizeof(errbuf)); \ 218 if (errbuf[0]) { \ 219 syslog(LOG_WARNING, "%s line %d: %s", \ 220 infile, (int)line, errbuf); \ 221 goto nextline; \ 222 } \ 223 curclass.Field = llval; \ 224 } while(0) 225 226 if (0) { 227 /* no-op */ 228 229 } else if ((strcasecmp(word, "advertise") == 0) 230 || (strcasecmp(word, "advertize") == 0)) { 231 struct addrinfo hints, *res; 232 int error; 233 234 memset((char *)&curclass.advertise, 0, 235 sizeof(curclass.advertise)); 236 curclass.advertise.su_len = 0; 237 if (none || EMPTYSTR(arg)) 238 continue; 239 res = NULL; 240 memset(&hints, 0, sizeof(hints)); 241 /* 242 * only get addresses of the family 243 * that we're listening on 244 */ 245 hints.ai_family = ctrl_addr.su_family; 246 hints.ai_socktype = SOCK_STREAM; 247 error = getaddrinfo(arg, "0", &hints, &res); 248 if (error) { 249 syslog(LOG_WARNING, "%s line %d: %s", 250 infile, (int)line, gai_strerror(error)); 251 advertiseparsefail: 252 if (res) 253 freeaddrinfo(res); 254 continue; 255 } 256 if (res->ai_next) { 257 syslog(LOG_WARNING, 258 "%s line %d: multiple addresses returned for `%s'; please be more specific", 259 infile, (int)line, arg); 260 goto advertiseparsefail; 261 } 262 if (sizeof(curclass.advertise) < res->ai_addrlen || ( 263#ifdef INET6 264 res->ai_family != AF_INET6 && 265#endif 266 res->ai_family != AF_INET)) { 267 syslog(LOG_WARNING, 268 "%s line %d: unsupported protocol %d for `%s'", 269 infile, (int)line, res->ai_family, arg); 270 goto advertiseparsefail; 271 } 272 memcpy(&curclass.advertise, res->ai_addr, 273 res->ai_addrlen); 274 curclass.advertise.su_len = res->ai_addrlen; 275 freeaddrinfo(res); 276 277 } else if (strcasecmp(word, "checkportcmd") == 0) { 278 CONF_FLAG(checkportcmd); 279 280 } else if (strcasecmp(word, "chroot") == 0) { 281 CONF_STRING(chroot); 282 283 } else if (strcasecmp(word, "classtype") == 0) { 284 if (!none && !EMPTYSTR(arg)) { 285 if (strcasecmp(arg, "GUEST") == 0) 286 curclass.type = CLASS_GUEST; 287 else if (strcasecmp(arg, "CHROOT") == 0) 288 curclass.type = CLASS_CHROOT; 289 else if (strcasecmp(arg, "REAL") == 0) 290 curclass.type = CLASS_REAL; 291 else { 292 syslog(LOG_WARNING, 293 "%s line %d: unknown class type `%s'", 294 infile, (int)line, arg); 295 continue; 296 } 297 } 298 299 } else if (strcasecmp(word, "conversion") == 0) { 300 char *suffix, *types, *disable, *convcmd; 301 302 if (EMPTYSTR(arg)) { 303 syslog(LOG_WARNING, 304 "%s line %d: %s requires a suffix", 305 infile, (int)line, word); 306 continue; /* need a suffix */ 307 } 308 NEXTWORD(p, types); 309 NEXTWORD(p, disable); 310 convcmd = p; 311 if (convcmd) 312 convcmd += strspn(convcmd, " \t");
| 210 REASSIGN(curclass.Field, arg); \ 211 } while (0) 212 213#define CONF_LL(Field,Arg,Min,Max) \ 214 do { \ 215 if (none || EMPTYSTR(Arg)) \ 216 goto nextline; \ 217 llval = strsuftollx(#Field, Arg, Min, Max, \ 218 errbuf, sizeof(errbuf)); \ 219 if (errbuf[0]) { \ 220 syslog(LOG_WARNING, "%s line %d: %s", \ 221 infile, (int)line, errbuf); \ 222 goto nextline; \ 223 } \ 224 curclass.Field = llval; \ 225 } while(0) 226 227 if (0) { 228 /* no-op */ 229 230 } else if ((strcasecmp(word, "advertise") == 0) 231 || (strcasecmp(word, "advertize") == 0)) { 232 struct addrinfo hints, *res; 233 int error; 234 235 memset((char *)&curclass.advertise, 0, 236 sizeof(curclass.advertise)); 237 curclass.advertise.su_len = 0; 238 if (none || EMPTYSTR(arg)) 239 continue; 240 res = NULL; 241 memset(&hints, 0, sizeof(hints)); 242 /* 243 * only get addresses of the family 244 * that we're listening on 245 */ 246 hints.ai_family = ctrl_addr.su_family; 247 hints.ai_socktype = SOCK_STREAM; 248 error = getaddrinfo(arg, "0", &hints, &res); 249 if (error) { 250 syslog(LOG_WARNING, "%s line %d: %s", 251 infile, (int)line, gai_strerror(error)); 252 advertiseparsefail: 253 if (res) 254 freeaddrinfo(res); 255 continue; 256 } 257 if (res->ai_next) { 258 syslog(LOG_WARNING, 259 "%s line %d: multiple addresses returned for `%s'; please be more specific", 260 infile, (int)line, arg); 261 goto advertiseparsefail; 262 } 263 if (sizeof(curclass.advertise) < res->ai_addrlen || ( 264#ifdef INET6 265 res->ai_family != AF_INET6 && 266#endif 267 res->ai_family != AF_INET)) { 268 syslog(LOG_WARNING, 269 "%s line %d: unsupported protocol %d for `%s'", 270 infile, (int)line, res->ai_family, arg); 271 goto advertiseparsefail; 272 } 273 memcpy(&curclass.advertise, res->ai_addr, 274 res->ai_addrlen); 275 curclass.advertise.su_len = res->ai_addrlen; 276 freeaddrinfo(res); 277 278 } else if (strcasecmp(word, "checkportcmd") == 0) { 279 CONF_FLAG(checkportcmd); 280 281 } else if (strcasecmp(word, "chroot") == 0) { 282 CONF_STRING(chroot); 283 284 } else if (strcasecmp(word, "classtype") == 0) { 285 if (!none && !EMPTYSTR(arg)) { 286 if (strcasecmp(arg, "GUEST") == 0) 287 curclass.type = CLASS_GUEST; 288 else if (strcasecmp(arg, "CHROOT") == 0) 289 curclass.type = CLASS_CHROOT; 290 else if (strcasecmp(arg, "REAL") == 0) 291 curclass.type = CLASS_REAL; 292 else { 293 syslog(LOG_WARNING, 294 "%s line %d: unknown class type `%s'", 295 infile, (int)line, arg); 296 continue; 297 } 298 } 299 300 } else if (strcasecmp(word, "conversion") == 0) { 301 char *suffix, *types, *disable, *convcmd; 302 303 if (EMPTYSTR(arg)) { 304 syslog(LOG_WARNING, 305 "%s line %d: %s requires a suffix", 306 infile, (int)line, word); 307 continue; /* need a suffix */ 308 } 309 NEXTWORD(p, types); 310 NEXTWORD(p, disable); 311 convcmd = p; 312 if (convcmd) 313 convcmd += strspn(convcmd, " \t");
|
313 suffix = xstrdup(arg);
| 314 suffix = ftpd_strdup(arg);
|
314 if (none || EMPTYSTR(types) || 315 EMPTYSTR(disable) || EMPTYSTR(convcmd)) { 316 types = NULL; 317 disable = NULL; 318 convcmd = NULL; 319 } else {
| 315 if (none || EMPTYSTR(types) || 316 EMPTYSTR(disable) || EMPTYSTR(convcmd)) { 317 types = NULL; 318 disable = NULL; 319 convcmd = NULL; 320 } else {
|
320 types = xstrdup(types); 321 disable = xstrdup(disable); 322 convcmd = xstrdup(convcmd);
| 321 types = ftpd_strdup(types); 322 disable = ftpd_strdup(disable); 323 convcmd = ftpd_strdup(convcmd);
|
323 } 324 for (conv = curclass.conversions; conv != NULL; 325 conv = conv->next) { 326 if (strcmp(conv->suffix, suffix) == 0) 327 break; 328 } 329 if (conv == NULL) { 330 conv = (struct ftpconv *) 331 calloc(1, sizeof(struct ftpconv)); 332 if (conv == NULL) { 333 syslog(LOG_WARNING, "can't malloc"); 334 continue; 335 } 336 conv->next = NULL; 337 for (cnext = curclass.conversions; 338 cnext != NULL; cnext = cnext->next) 339 if (cnext->next == NULL) 340 break; 341 if (cnext != NULL) 342 cnext->next = conv; 343 else 344 curclass.conversions = conv; 345 } 346 REASSIGN(conv->suffix, suffix); 347 REASSIGN(conv->types, types); 348 REASSIGN(conv->disable, disable); 349 REASSIGN(conv->command, convcmd); 350 351 } else if (strcasecmp(word, "denyquick") == 0) { 352 CONF_FLAG(denyquick); 353 354 } else if (strcasecmp(word, "display") == 0) { 355 CONF_STRING(display); 356
| 324 } 325 for (conv = curclass.conversions; conv != NULL; 326 conv = conv->next) { 327 if (strcmp(conv->suffix, suffix) == 0) 328 break; 329 } 330 if (conv == NULL) { 331 conv = (struct ftpconv *) 332 calloc(1, sizeof(struct ftpconv)); 333 if (conv == NULL) { 334 syslog(LOG_WARNING, "can't malloc"); 335 continue; 336 } 337 conv->next = NULL; 338 for (cnext = curclass.conversions; 339 cnext != NULL; cnext = cnext->next) 340 if (cnext->next == NULL) 341 break; 342 if (cnext != NULL) 343 cnext->next = conv; 344 else 345 curclass.conversions = conv; 346 } 347 REASSIGN(conv->suffix, suffix); 348 REASSIGN(conv->types, types); 349 REASSIGN(conv->disable, disable); 350 REASSIGN(conv->command, convcmd); 351 352 } else if (strcasecmp(word, "denyquick") == 0) { 353 CONF_FLAG(denyquick); 354 355 } else if (strcasecmp(word, "display") == 0) { 356 CONF_STRING(display); 357
|
| 358 } else if (strcasecmp(word, "hidesymlinks") == 0) { 359 CONF_FLAG(hidesymlinks); 360
|
357 } else if (strcasecmp(word, "homedir") == 0) { 358 CONF_STRING(homedir); 359 360 } else if (strcasecmp(word, "limit") == 0) { 361 curclass.limit = DEFAULT_LIMIT; 362 REASSIGN(curclass.limitfile, NULL); 363 CONF_LL(limit, arg, -1, LLTMAX); 364 REASSIGN(curclass.limitfile,
| 361 } else if (strcasecmp(word, "homedir") == 0) { 362 CONF_STRING(homedir); 363 364 } else if (strcasecmp(word, "limit") == 0) { 365 curclass.limit = DEFAULT_LIMIT; 366 REASSIGN(curclass.limitfile, NULL); 367 CONF_LL(limit, arg, -1, LLTMAX); 368 REASSIGN(curclass.limitfile,
|
365 EMPTYSTR(p) ? NULL : xstrdup(p));
| 369 EMPTYSTR(p) ? NULL : ftpd_strdup(p));
|
366 367 } else if (strcasecmp(word, "maxfilesize") == 0) { 368 curclass.maxfilesize = DEFAULT_MAXFILESIZE; 369 CONF_LL(maxfilesize, arg, -1, LLTMAX); 370 371 } else if (strcasecmp(word, "maxtimeout") == 0) { 372 curclass.maxtimeout = DEFAULT_MAXTIMEOUT; 373 CONF_LL(maxtimeout, arg, 374 MIN(30, curclass.timeout), LLTMAX); 375 376 } else if (strcasecmp(word, "mmapsize") == 0) { 377 curclass.mmapsize = 0; 378 CONF_LL(mmapsize, arg, 0, LLTMAX); 379 380 } else if (strcasecmp(word, "readsize") == 0) { 381 curclass.readsize = 0; 382 CONF_LL(readsize, arg, 0, LLTMAX); 383 384 } else if (strcasecmp(word, "writesize") == 0) { 385 curclass.writesize = 0; 386 CONF_LL(writesize, arg, 0, LLTMAX); 387
| 370 371 } else if (strcasecmp(word, "maxfilesize") == 0) { 372 curclass.maxfilesize = DEFAULT_MAXFILESIZE; 373 CONF_LL(maxfilesize, arg, -1, LLTMAX); 374 375 } else if (strcasecmp(word, "maxtimeout") == 0) { 376 curclass.maxtimeout = DEFAULT_MAXTIMEOUT; 377 CONF_LL(maxtimeout, arg, 378 MIN(30, curclass.timeout), LLTMAX); 379 380 } else if (strcasecmp(word, "mmapsize") == 0) { 381 curclass.mmapsize = 0; 382 CONF_LL(mmapsize, arg, 0, LLTMAX); 383 384 } else if (strcasecmp(word, "readsize") == 0) { 385 curclass.readsize = 0; 386 CONF_LL(readsize, arg, 0, LLTMAX); 387 388 } else if (strcasecmp(word, "writesize") == 0) { 389 curclass.writesize = 0; 390 CONF_LL(writesize, arg, 0, LLTMAX); 391
|
| 392 } else if (strcasecmp(word, "recvbufsize") == 0) { 393 curclass.recvbufsize = 0; 394 CONF_LL(recvbufsize, arg, 0, LLTMAX); 395
|
388 } else if (strcasecmp(word, "sendbufsize") == 0) { 389 curclass.sendbufsize = 0; 390 CONF_LL(sendbufsize, arg, 0, LLTMAX); 391 392 } else if (strcasecmp(word, "sendlowat") == 0) { 393 curclass.sendlowat = 0; 394 CONF_LL(sendlowat, arg, 0, LLTMAX); 395 396 } else if (strcasecmp(word, "modify") == 0) { 397 CONF_FLAG(modify); 398 399 } else if (strcasecmp(word, "motd") == 0) { 400 CONF_STRING(motd); 401 402 } else if (strcasecmp(word, "notify") == 0) { 403 CONF_STRING(notify); 404 405 } else if (strcasecmp(word, "passive") == 0) { 406 CONF_FLAG(passive); 407 408 } else if (strcasecmp(word, "portrange") == 0) { 409 long minport, maxport; 410 411 curclass.portmin = 0; 412 curclass.portmax = 0; 413 if (none || EMPTYSTR(arg)) 414 continue; 415 if (EMPTYSTR(p)) { 416 syslog(LOG_WARNING, 417 "%s line %d: missing maxport argument", 418 infile, (int)line); 419 continue; 420 } 421 minport = strsuftollx("minport", arg, IPPORT_RESERVED, 422 IPPORT_ANONMAX, errbuf, sizeof(errbuf)); 423 if (errbuf[0]) { 424 syslog(LOG_WARNING, "%s line %d: %s", 425 infile, (int)line, errbuf); 426 continue; 427 } 428 maxport = strsuftollx("maxport", p, IPPORT_RESERVED, 429 IPPORT_ANONMAX, errbuf, sizeof(errbuf)); 430 if (errbuf[0]) { 431 syslog(LOG_WARNING, "%s line %d: %s", 432 infile, (int)line, errbuf); 433 continue; 434 } 435 if (minport >= maxport) { 436 syslog(LOG_WARNING, 437 "%s line %d: minport %ld >= maxport %ld", 438 infile, (int)line, minport, maxport); 439 continue; 440 } 441 curclass.portmin = (int)minport; 442 curclass.portmax = (int)maxport; 443 444 } else if (strcasecmp(word, "private") == 0) { 445 CONF_FLAG(private); 446 447 } else if (strcasecmp(word, "rateget") == 0) { 448 curclass.maxrateget = curclass.rateget = 0; 449 CONF_LL(rateget, arg, 0, LLTMAX); 450 curclass.maxrateget = curclass.rateget; 451 452 } else if (strcasecmp(word, "rateput") == 0) { 453 curclass.maxrateput = curclass.rateput = 0; 454 CONF_LL(rateput, arg, 0, LLTMAX); 455 curclass.maxrateput = curclass.rateput; 456 457 } else if (strcasecmp(word, "sanenames") == 0) { 458 CONF_FLAG(sanenames); 459 460 } else if (strcasecmp(word, "timeout") == 0) { 461 curclass.timeout = DEFAULT_TIMEOUT; 462 CONF_LL(timeout, arg, 30, curclass.maxtimeout); 463 464 } else if (strcasecmp(word, "template") == 0) { 465 if (none) 466 continue;
| 396 } else if (strcasecmp(word, "sendbufsize") == 0) { 397 curclass.sendbufsize = 0; 398 CONF_LL(sendbufsize, arg, 0, LLTMAX); 399 400 } else if (strcasecmp(word, "sendlowat") == 0) { 401 curclass.sendlowat = 0; 402 CONF_LL(sendlowat, arg, 0, LLTMAX); 403 404 } else if (strcasecmp(word, "modify") == 0) { 405 CONF_FLAG(modify); 406 407 } else if (strcasecmp(word, "motd") == 0) { 408 CONF_STRING(motd); 409 410 } else if (strcasecmp(word, "notify") == 0) { 411 CONF_STRING(notify); 412 413 } else if (strcasecmp(word, "passive") == 0) { 414 CONF_FLAG(passive); 415 416 } else if (strcasecmp(word, "portrange") == 0) { 417 long minport, maxport; 418 419 curclass.portmin = 0; 420 curclass.portmax = 0; 421 if (none || EMPTYSTR(arg)) 422 continue; 423 if (EMPTYSTR(p)) { 424 syslog(LOG_WARNING, 425 "%s line %d: missing maxport argument", 426 infile, (int)line); 427 continue; 428 } 429 minport = strsuftollx("minport", arg, IPPORT_RESERVED, 430 IPPORT_ANONMAX, errbuf, sizeof(errbuf)); 431 if (errbuf[0]) { 432 syslog(LOG_WARNING, "%s line %d: %s", 433 infile, (int)line, errbuf); 434 continue; 435 } 436 maxport = strsuftollx("maxport", p, IPPORT_RESERVED, 437 IPPORT_ANONMAX, errbuf, sizeof(errbuf)); 438 if (errbuf[0]) { 439 syslog(LOG_WARNING, "%s line %d: %s", 440 infile, (int)line, errbuf); 441 continue; 442 } 443 if (minport >= maxport) { 444 syslog(LOG_WARNING, 445 "%s line %d: minport %ld >= maxport %ld", 446 infile, (int)line, minport, maxport); 447 continue; 448 } 449 curclass.portmin = (int)minport; 450 curclass.portmax = (int)maxport; 451 452 } else if (strcasecmp(word, "private") == 0) { 453 CONF_FLAG(private); 454 455 } else if (strcasecmp(word, "rateget") == 0) { 456 curclass.maxrateget = curclass.rateget = 0; 457 CONF_LL(rateget, arg, 0, LLTMAX); 458 curclass.maxrateget = curclass.rateget; 459 460 } else if (strcasecmp(word, "rateput") == 0) { 461 curclass.maxrateput = curclass.rateput = 0; 462 CONF_LL(rateput, arg, 0, LLTMAX); 463 curclass.maxrateput = curclass.rateput; 464 465 } else if (strcasecmp(word, "sanenames") == 0) { 466 CONF_FLAG(sanenames); 467 468 } else if (strcasecmp(word, "timeout") == 0) { 469 curclass.timeout = DEFAULT_TIMEOUT; 470 CONF_LL(timeout, arg, 30, curclass.maxtimeout); 471 472 } else if (strcasecmp(word, "template") == 0) { 473 if (none) 474 continue;
|
467 REASSIGN(template, EMPTYSTR(arg) ? NULL : xstrdup(arg));
| 475 REASSIGN(template, EMPTYSTR(arg) ? NULL : ftpd_strdup(arg));
|
468 469 } else if (strcasecmp(word, "umask") == 0) { 470 u_long fumask; 471 472 curclass.umask = DEFAULT_UMASK; 473 if (none || EMPTYSTR(arg)) 474 continue; 475 errno = 0; 476 endp = NULL; 477 fumask = strtoul(arg, &endp, 8); 478 if (errno || *arg == '\0' || *endp != '\0' || 479 fumask > 0777) { 480 syslog(LOG_WARNING, 481 "%s line %d: invalid umask %s", 482 infile, (int)line, arg); 483 continue; 484 } 485 curclass.umask = (mode_t)fumask; 486 487 } else if (strcasecmp(word, "upload") == 0) { 488 CONF_FLAG(upload); 489 if (! CURCLASS_FLAGS_ISSET(upload)) 490 CURCLASS_FLAGS_CLR(modify); 491 492 } else { 493 syslog(LOG_WARNING, 494 "%s line %d: unknown directive '%s'", 495 infile, (int)line, word); 496 continue; 497 } 498 nextline: 499 ; 500 } 501 REASSIGN(template, NULL); 502 fclose(f); 503} 504 505/* 506 * Show file listed in curclass.display first time in, and list all the 507 * files named in curclass.notify in the current directory. 508 * Send back responses with the prefix `code' + "-". 509 * If code == -1, flush the internal cache of directory names and return. 510 */ 511void 512show_chdir_messages(int code) 513{ 514 static StringList *slist = NULL; 515 516 struct stat st; 517 struct tm *t; 518 glob_t gl; 519 time_t now, then; 520 int age; 521 char curwd[MAXPATHLEN]; 522 char *cp, **rlist; 523 524 if (code == -1) { 525 if (slist != NULL) 526 sl_free(slist, 1); 527 slist = NULL; 528 return; 529 } 530 531 if (quietmessages) 532 return; 533 534 /* Setup list for directory cache */ 535 if (slist == NULL) 536 slist = sl_init(); 537 if (slist == NULL) { 538 syslog(LOG_WARNING, "can't allocate memory for stringlist"); 539 return; 540 } 541 542 /* Check if this directory has already been visited */ 543 if (getcwd(curwd, sizeof(curwd) - 1) == NULL) { 544 syslog(LOG_WARNING, "can't getcwd: %s", strerror(errno)); 545 return; 546 } 547 if (sl_find(slist, curwd) != NULL) 548 return; 549
| 476 477 } else if (strcasecmp(word, "umask") == 0) { 478 u_long fumask; 479 480 curclass.umask = DEFAULT_UMASK; 481 if (none || EMPTYSTR(arg)) 482 continue; 483 errno = 0; 484 endp = NULL; 485 fumask = strtoul(arg, &endp, 8); 486 if (errno || *arg == '\0' || *endp != '\0' || 487 fumask > 0777) { 488 syslog(LOG_WARNING, 489 "%s line %d: invalid umask %s", 490 infile, (int)line, arg); 491 continue; 492 } 493 curclass.umask = (mode_t)fumask; 494 495 } else if (strcasecmp(word, "upload") == 0) { 496 CONF_FLAG(upload); 497 if (! CURCLASS_FLAGS_ISSET(upload)) 498 CURCLASS_FLAGS_CLR(modify); 499 500 } else { 501 syslog(LOG_WARNING, 502 "%s line %d: unknown directive '%s'", 503 infile, (int)line, word); 504 continue; 505 } 506 nextline: 507 ; 508 } 509 REASSIGN(template, NULL); 510 fclose(f); 511} 512 513/* 514 * Show file listed in curclass.display first time in, and list all the 515 * files named in curclass.notify in the current directory. 516 * Send back responses with the prefix `code' + "-". 517 * If code == -1, flush the internal cache of directory names and return. 518 */ 519void 520show_chdir_messages(int code) 521{ 522 static StringList *slist = NULL; 523 524 struct stat st; 525 struct tm *t; 526 glob_t gl; 527 time_t now, then; 528 int age; 529 char curwd[MAXPATHLEN]; 530 char *cp, **rlist; 531 532 if (code == -1) { 533 if (slist != NULL) 534 sl_free(slist, 1); 535 slist = NULL; 536 return; 537 } 538 539 if (quietmessages) 540 return; 541 542 /* Setup list for directory cache */ 543 if (slist == NULL) 544 slist = sl_init(); 545 if (slist == NULL) { 546 syslog(LOG_WARNING, "can't allocate memory for stringlist"); 547 return; 548 } 549 550 /* Check if this directory has already been visited */ 551 if (getcwd(curwd, sizeof(curwd) - 1) == NULL) { 552 syslog(LOG_WARNING, "can't getcwd: %s", strerror(errno)); 553 return; 554 } 555 if (sl_find(slist, curwd) != NULL) 556 return; 557
|
550 cp = xstrdup(curwd);
| 558 cp = ftpd_strdup(curwd);
|
551 if (sl_add(slist, cp) == -1) 552 syslog(LOG_WARNING, "can't add `%s' to stringlist", cp); 553 554 /* First check for a display file */ 555 (void)display_file(curclass.display, code); 556 557 /* Now see if there are any notify files */ 558 if (EMPTYSTR(curclass.notify)) 559 return; 560 561 memset(&gl, 0, sizeof(gl)); 562 if (glob(curclass.notify, GLOB_BRACE|GLOB_LIMIT, NULL, &gl) != 0 563 || gl.gl_matchc == 0) { 564 globfree(&gl); 565 return; 566 } 567 time(&now); 568 for (rlist = gl.gl_pathv; *rlist != NULL; rlist++) { 569 if (stat(*rlist, &st) != 0) 570 continue; 571 if (!S_ISREG(st.st_mode)) 572 continue; 573 then = st.st_mtime; 574 if (code != 0) { 575 reply(-code, "%s", ""); 576 code = 0; 577 } 578 reply(-code, "Please read the file %s", *rlist); 579 t = localtime(&now); 580 age = 365 * t->tm_year + t->tm_yday; 581 t = localtime(&then); 582 age -= 365 * t->tm_year + t->tm_yday; 583 reply(-code, " it was last modified on %.24s - %d day%s ago", 584 ctime(&then), age, PLURAL(age)); 585 } 586 globfree(&gl); 587} 588 589int 590display_file(const char *file, int code) 591{ 592 FILE *f; 593 char *buf, *p; 594 char curwd[MAXPATHLEN]; 595 size_t len; 596 off_t lastnum; 597 time_t now; 598 599 lastnum = 0; 600 if (quietmessages) 601 return (0); 602 603 if (EMPTYSTR(file)) 604 return(0); 605 if ((f = fopen(file, "r")) == NULL) 606 return (0); 607 reply(-code, "%s", ""); 608 609 for (; 610 (buf = fparseln(f, &len, NULL, "\0\0\0", 0)) != NULL; free(buf)) { 611 if (len > 0) 612 if (buf[len - 1] == '\n') 613 buf[--len] = '\0'; 614 cprintf(stdout, " "); 615 616 for (p = buf; *p; p++) { 617 if (*p == '%') { 618 p++; 619 switch (*p) { 620 621 case 'c': 622 cprintf(stdout, "%s", 623 curclass.classname ? 624 curclass.classname : "<unknown>"); 625 break; 626 627 case 'C': 628 if (getcwd(curwd, sizeof(curwd)-1) 629 == NULL){ 630 syslog(LOG_WARNING, 631 "can't getcwd: %s", 632 strerror(errno)); 633 continue; 634 } 635 cprintf(stdout, "%s", curwd); 636 break; 637 638 case 'E': 639 if (! EMPTYSTR(emailaddr)) 640 cprintf(stdout, "%s", 641 emailaddr); 642 break; 643 644 case 'L': 645 cprintf(stdout, "%s", hostname); 646 break; 647 648 case 'M': 649 if (curclass.limit == -1) { 650 cprintf(stdout, "unlimited"); 651 lastnum = 0; 652 } else { 653 cprintf(stdout, LLF, 654 (LLT)curclass.limit); 655 lastnum = curclass.limit; 656 } 657 break; 658 659 case 'N': 660 cprintf(stdout, "%d", connections); 661 lastnum = connections; 662 break; 663 664 case 'R': 665 cprintf(stdout, "%s", remotehost); 666 break; 667 668 case 's': 669 if (lastnum != 1) 670 cprintf(stdout, "s"); 671 break; 672 673 case 'S': 674 if (lastnum != 1) 675 cprintf(stdout, "S"); 676 break; 677 678 case 'T': 679 now = time(NULL); 680 cprintf(stdout, "%.24s", ctime(&now)); 681 break; 682 683 case 'U': 684 cprintf(stdout, "%s", 685 pw ? pw->pw_name : "<unknown>"); 686 break; 687 688 case '%': 689 CPUTC('%', stdout); 690 break; 691 692 } 693 } else 694 CPUTC(*p, stdout); 695 } 696 cprintf(stdout, "\r\n"); 697 } 698 699 (void)fflush(stdout); 700 (void)fclose(f); 701 return (1); 702} 703 704/* 705 * Parse src, expanding '%' escapes, into dst (which must be at least 706 * MAXPATHLEN long). 707 */ 708void 709format_path(char *dst, const char *src) 710{ 711 size_t len; 712 const char *p; 713 714 dst[0] = '\0'; 715 len = 0; 716 if (src == NULL) 717 return; 718 for (p = src; *p && len < MAXPATHLEN; p++) { 719 if (*p == '%') { 720 p++; 721 switch (*p) { 722 723 case 'c': 724 len += strlcpy(dst + len, curclass.classname, 725 MAXPATHLEN - len); 726 break; 727 728 case 'd': 729 len += strlcpy(dst + len, pw->pw_dir, 730 MAXPATHLEN - len); 731 break; 732 733 case 'u': 734 len += strlcpy(dst + len, pw->pw_name, 735 MAXPATHLEN - len); 736 break; 737 738 case '%': 739 dst[len++] = '%'; 740 break; 741 742 } 743 } else 744 dst[len++] = *p; 745 } 746 if (len < MAXPATHLEN) 747 dst[len] = '\0'; 748 dst[MAXPATHLEN - 1] = '\0'; 749} 750 751/* 752 * Find s2 at the end of s1. If found, return a string up to (but 753 * not including) s2, otherwise returns NULL. 754 */ 755static char * 756strend(const char *s1, char *s2) 757{ 758 static char buf[MAXPATHLEN]; 759 760 char *start; 761 size_t l1, l2; 762 763 l1 = strlen(s1); 764 l2 = strlen(s2); 765 766 if (l2 >= l1 || l1 >= sizeof(buf)) 767 return(NULL); 768 769 strlcpy(buf, s1, sizeof(buf)); 770 start = buf + (l1 - l2); 771 772 if (strcmp(start, s2) == 0) { 773 *start = '\0'; 774 return(buf); 775 } else 776 return(NULL); 777} 778 779static int 780filetypematch(char *types, int mode) 781{ 782 for ( ; types[0] != '\0'; types++) 783 switch (*types) { 784 case 'd': 785 if (S_ISDIR(mode)) 786 return(1); 787 break; 788 case 'f': 789 if (S_ISREG(mode)) 790 return(1); 791 break; 792 } 793 return(0); 794} 795 796/* 797 * Look for a conversion. If we succeed, return a pointer to the 798 * command to execute for the conversion. 799 * 800 * The command is stored in a static array so there's no memory 801 * leak problems, and not too much to change in ftpd.c. This 802 * routine doesn't need to be re-entrant unless we start using a 803 * multi-threaded ftpd, and that's not likely for a while... 804 */ 805char ** 806do_conversion(const char *fname) 807{ 808 struct ftpconv *cp; 809 struct stat st; 810 int o_errno; 811 char *base = NULL; 812 char *cmd, *p, *lp, **argv; 813 StringList *sl; 814 815 o_errno = errno; 816 sl = NULL; 817 cmd = NULL; 818 for (cp = curclass.conversions; cp != NULL; cp = cp->next) { 819 if (cp->suffix == NULL) { 820 syslog(LOG_WARNING, 821 "cp->suffix==NULL in conv list; SHOULDN'T HAPPEN!"); 822 continue; 823 } 824 if ((base = strend(fname, cp->suffix)) == NULL) 825 continue; 826 if (cp->types == NULL || cp->disable == NULL || 827 cp->command == NULL) 828 continue; 829 /* Is it enabled? */ 830 if (strcmp(cp->disable, ".") != 0 && 831 stat(cp->disable, &st) == 0) 832 continue; 833 /* Does the base exist? */ 834 if (stat(base, &st) < 0) 835 continue; 836 /* Is the file type ok */ 837 if (!filetypematch(cp->types, st.st_mode)) 838 continue; 839 break; /* "We have a winner!" */ 840 } 841 842 /* If we got through the list, no conversion */ 843 if (cp == NULL) 844 goto cleanup_do_conv; 845 846 /* Split up command into an argv */ 847 if ((sl = sl_init()) == NULL) 848 goto cleanup_do_conv;
| 559 if (sl_add(slist, cp) == -1) 560 syslog(LOG_WARNING, "can't add `%s' to stringlist", cp); 561 562 /* First check for a display file */ 563 (void)display_file(curclass.display, code); 564 565 /* Now see if there are any notify files */ 566 if (EMPTYSTR(curclass.notify)) 567 return; 568 569 memset(&gl, 0, sizeof(gl)); 570 if (glob(curclass.notify, GLOB_BRACE|GLOB_LIMIT, NULL, &gl) != 0 571 || gl.gl_matchc == 0) { 572 globfree(&gl); 573 return; 574 } 575 time(&now); 576 for (rlist = gl.gl_pathv; *rlist != NULL; rlist++) { 577 if (stat(*rlist, &st) != 0) 578 continue; 579 if (!S_ISREG(st.st_mode)) 580 continue; 581 then = st.st_mtime; 582 if (code != 0) { 583 reply(-code, "%s", ""); 584 code = 0; 585 } 586 reply(-code, "Please read the file %s", *rlist); 587 t = localtime(&now); 588 age = 365 * t->tm_year + t->tm_yday; 589 t = localtime(&then); 590 age -= 365 * t->tm_year + t->tm_yday; 591 reply(-code, " it was last modified on %.24s - %d day%s ago", 592 ctime(&then), age, PLURAL(age)); 593 } 594 globfree(&gl); 595} 596 597int 598display_file(const char *file, int code) 599{ 600 FILE *f; 601 char *buf, *p; 602 char curwd[MAXPATHLEN]; 603 size_t len; 604 off_t lastnum; 605 time_t now; 606 607 lastnum = 0; 608 if (quietmessages) 609 return (0); 610 611 if (EMPTYSTR(file)) 612 return(0); 613 if ((f = fopen(file, "r")) == NULL) 614 return (0); 615 reply(-code, "%s", ""); 616 617 for (; 618 (buf = fparseln(f, &len, NULL, "\0\0\0", 0)) != NULL; free(buf)) { 619 if (len > 0) 620 if (buf[len - 1] == '\n') 621 buf[--len] = '\0'; 622 cprintf(stdout, " "); 623 624 for (p = buf; *p; p++) { 625 if (*p == '%') { 626 p++; 627 switch (*p) { 628 629 case 'c': 630 cprintf(stdout, "%s", 631 curclass.classname ? 632 curclass.classname : "<unknown>"); 633 break; 634 635 case 'C': 636 if (getcwd(curwd, sizeof(curwd)-1) 637 == NULL){ 638 syslog(LOG_WARNING, 639 "can't getcwd: %s", 640 strerror(errno)); 641 continue; 642 } 643 cprintf(stdout, "%s", curwd); 644 break; 645 646 case 'E': 647 if (! EMPTYSTR(emailaddr)) 648 cprintf(stdout, "%s", 649 emailaddr); 650 break; 651 652 case 'L': 653 cprintf(stdout, "%s", hostname); 654 break; 655 656 case 'M': 657 if (curclass.limit == -1) { 658 cprintf(stdout, "unlimited"); 659 lastnum = 0; 660 } else { 661 cprintf(stdout, LLF, 662 (LLT)curclass.limit); 663 lastnum = curclass.limit; 664 } 665 break; 666 667 case 'N': 668 cprintf(stdout, "%d", connections); 669 lastnum = connections; 670 break; 671 672 case 'R': 673 cprintf(stdout, "%s", remotehost); 674 break; 675 676 case 's': 677 if (lastnum != 1) 678 cprintf(stdout, "s"); 679 break; 680 681 case 'S': 682 if (lastnum != 1) 683 cprintf(stdout, "S"); 684 break; 685 686 case 'T': 687 now = time(NULL); 688 cprintf(stdout, "%.24s", ctime(&now)); 689 break; 690 691 case 'U': 692 cprintf(stdout, "%s", 693 pw ? pw->pw_name : "<unknown>"); 694 break; 695 696 case '%': 697 CPUTC('%', stdout); 698 break; 699 700 } 701 } else 702 CPUTC(*p, stdout); 703 } 704 cprintf(stdout, "\r\n"); 705 } 706 707 (void)fflush(stdout); 708 (void)fclose(f); 709 return (1); 710} 711 712/* 713 * Parse src, expanding '%' escapes, into dst (which must be at least 714 * MAXPATHLEN long). 715 */ 716void 717format_path(char *dst, const char *src) 718{ 719 size_t len; 720 const char *p; 721 722 dst[0] = '\0'; 723 len = 0; 724 if (src == NULL) 725 return; 726 for (p = src; *p && len < MAXPATHLEN; p++) { 727 if (*p == '%') { 728 p++; 729 switch (*p) { 730 731 case 'c': 732 len += strlcpy(dst + len, curclass.classname, 733 MAXPATHLEN - len); 734 break; 735 736 case 'd': 737 len += strlcpy(dst + len, pw->pw_dir, 738 MAXPATHLEN - len); 739 break; 740 741 case 'u': 742 len += strlcpy(dst + len, pw->pw_name, 743 MAXPATHLEN - len); 744 break; 745 746 case '%': 747 dst[len++] = '%'; 748 break; 749 750 } 751 } else 752 dst[len++] = *p; 753 } 754 if (len < MAXPATHLEN) 755 dst[len] = '\0'; 756 dst[MAXPATHLEN - 1] = '\0'; 757} 758 759/* 760 * Find s2 at the end of s1. If found, return a string up to (but 761 * not including) s2, otherwise returns NULL. 762 */ 763static char * 764strend(const char *s1, char *s2) 765{ 766 static char buf[MAXPATHLEN]; 767 768 char *start; 769 size_t l1, l2; 770 771 l1 = strlen(s1); 772 l2 = strlen(s2); 773 774 if (l2 >= l1 || l1 >= sizeof(buf)) 775 return(NULL); 776 777 strlcpy(buf, s1, sizeof(buf)); 778 start = buf + (l1 - l2); 779 780 if (strcmp(start, s2) == 0) { 781 *start = '\0'; 782 return(buf); 783 } else 784 return(NULL); 785} 786 787static int 788filetypematch(char *types, int mode) 789{ 790 for ( ; types[0] != '\0'; types++) 791 switch (*types) { 792 case 'd': 793 if (S_ISDIR(mode)) 794 return(1); 795 break; 796 case 'f': 797 if (S_ISREG(mode)) 798 return(1); 799 break; 800 } 801 return(0); 802} 803 804/* 805 * Look for a conversion. If we succeed, return a pointer to the 806 * command to execute for the conversion. 807 * 808 * The command is stored in a static array so there's no memory 809 * leak problems, and not too much to change in ftpd.c. This 810 * routine doesn't need to be re-entrant unless we start using a 811 * multi-threaded ftpd, and that's not likely for a while... 812 */ 813char ** 814do_conversion(const char *fname) 815{ 816 struct ftpconv *cp; 817 struct stat st; 818 int o_errno; 819 char *base = NULL; 820 char *cmd, *p, *lp, **argv; 821 StringList *sl; 822 823 o_errno = errno; 824 sl = NULL; 825 cmd = NULL; 826 for (cp = curclass.conversions; cp != NULL; cp = cp->next) { 827 if (cp->suffix == NULL) { 828 syslog(LOG_WARNING, 829 "cp->suffix==NULL in conv list; SHOULDN'T HAPPEN!"); 830 continue; 831 } 832 if ((base = strend(fname, cp->suffix)) == NULL) 833 continue; 834 if (cp->types == NULL || cp->disable == NULL || 835 cp->command == NULL) 836 continue; 837 /* Is it enabled? */ 838 if (strcmp(cp->disable, ".") != 0 && 839 stat(cp->disable, &st) == 0) 840 continue; 841 /* Does the base exist? */ 842 if (stat(base, &st) < 0) 843 continue; 844 /* Is the file type ok */ 845 if (!filetypematch(cp->types, st.st_mode)) 846 continue; 847 break; /* "We have a winner!" */ 848 } 849 850 /* If we got through the list, no conversion */ 851 if (cp == NULL) 852 goto cleanup_do_conv; 853 854 /* Split up command into an argv */ 855 if ((sl = sl_init()) == NULL) 856 goto cleanup_do_conv;
|
849 cmd = xstrdup(cp->command);
| 857 cmd = ftpd_strdup(cp->command);
|
850 p = cmd; 851 while (p) { 852 NEXTWORD(p, lp); 853 if (strcmp(lp, "%s") == 0) 854 lp = base;
| 858 p = cmd; 859 while (p) { 860 NEXTWORD(p, lp); 861 if (strcmp(lp, "%s") == 0) 862 lp = base;
|
855 if (sl_add(sl, xstrdup(lp)) == -1)
| 863 if (sl_add(sl, ftpd_strdup(lp)) == -1)
|
856 goto cleanup_do_conv; 857 } 858 859 if (sl_add(sl, NULL) == -1) 860 goto cleanup_do_conv; 861 argv = sl->sl_str; 862 free(cmd); 863 free(sl); 864 return(argv); 865 866 cleanup_do_conv: 867 if (sl) 868 sl_free(sl, 1); 869 free(cmd); 870 errno = o_errno; 871 return(NULL); 872} 873 874/* 875 * Count the number of current connections, reading from 876 * /var/run/ftpd.pids-<class> 877 * Does a kill -0 on each pid in that file, and only counts 878 * processes that exist (or frees the slot if it doesn't). 879 * Adds getpid() to the first free slot. Truncates the file 880 * if possible. 881 */ 882void 883count_users(void) 884{ 885 char fn[MAXPATHLEN]; 886 int fd, i, last; 887 size_t count; 888 pid_t *pids, mypid; 889 struct stat sb; 890 891 (void)strlcpy(fn, _PATH_CLASSPIDS, sizeof(fn)); 892 (void)strlcat(fn, curclass.classname, sizeof(fn)); 893 pids = NULL; 894 connections = 1; 895 896 if ((fd = open(fn, O_RDWR | O_CREAT, 0600)) == -1) 897 return; 898 if (lockf(fd, F_TLOCK, 0) == -1) 899 goto cleanup_count; 900 if (fstat(fd, &sb) == -1) 901 goto cleanup_count; 902 if ((pids = malloc(sb.st_size + sizeof(pid_t))) == NULL) 903 goto cleanup_count; 904 count = read(fd, pids, sb.st_size); 905 if (count < 0 || count != sb.st_size) 906 goto cleanup_count; 907 count /= sizeof(pid_t); 908 mypid = getpid(); 909 last = 0; 910 for (i = 0; i < count; i++) { 911 if (pids[i] == 0) 912 continue; 913 if (kill(pids[i], 0) == -1 && errno != EPERM) { 914 if (mypid != 0) { 915 pids[i] = mypid; 916 mypid = 0; 917 last = i; 918 } 919 } else { 920 connections++; 921 last = i; 922 } 923 } 924 if (mypid != 0) { 925 if (pids[last] != 0) 926 last++; 927 pids[last] = mypid; 928 } 929 count = (last + 1) * sizeof(pid_t); 930 if (lseek(fd, 0, SEEK_SET) == -1) 931 goto cleanup_count; 932 if (write(fd, pids, count) == -1) 933 goto cleanup_count; 934 (void)ftruncate(fd, count); 935 936 cleanup_count: 937 if (lseek(fd, 0, SEEK_SET) != -1) 938 (void)lockf(fd, F_ULOCK, 0); 939 close(fd); 940 REASSIGN(pids, NULL); 941}
| 864 goto cleanup_do_conv; 865 } 866 867 if (sl_add(sl, NULL) == -1) 868 goto cleanup_do_conv; 869 argv = sl->sl_str; 870 free(cmd); 871 free(sl); 872 return(argv); 873 874 cleanup_do_conv: 875 if (sl) 876 sl_free(sl, 1); 877 free(cmd); 878 errno = o_errno; 879 return(NULL); 880} 881 882/* 883 * Count the number of current connections, reading from 884 * /var/run/ftpd.pids-<class> 885 * Does a kill -0 on each pid in that file, and only counts 886 * processes that exist (or frees the slot if it doesn't). 887 * Adds getpid() to the first free slot. Truncates the file 888 * if possible. 889 */ 890void 891count_users(void) 892{ 893 char fn[MAXPATHLEN]; 894 int fd, i, last; 895 size_t count; 896 pid_t *pids, mypid; 897 struct stat sb; 898 899 (void)strlcpy(fn, _PATH_CLASSPIDS, sizeof(fn)); 900 (void)strlcat(fn, curclass.classname, sizeof(fn)); 901 pids = NULL; 902 connections = 1; 903 904 if ((fd = open(fn, O_RDWR | O_CREAT, 0600)) == -1) 905 return; 906 if (lockf(fd, F_TLOCK, 0) == -1) 907 goto cleanup_count; 908 if (fstat(fd, &sb) == -1) 909 goto cleanup_count; 910 if ((pids = malloc(sb.st_size + sizeof(pid_t))) == NULL) 911 goto cleanup_count; 912 count = read(fd, pids, sb.st_size); 913 if (count < 0 || count != sb.st_size) 914 goto cleanup_count; 915 count /= sizeof(pid_t); 916 mypid = getpid(); 917 last = 0; 918 for (i = 0; i < count; i++) { 919 if (pids[i] == 0) 920 continue; 921 if (kill(pids[i], 0) == -1 && errno != EPERM) { 922 if (mypid != 0) { 923 pids[i] = mypid; 924 mypid = 0; 925 last = i; 926 } 927 } else { 928 connections++; 929 last = i; 930 } 931 } 932 if (mypid != 0) { 933 if (pids[last] != 0) 934 last++; 935 pids[last] = mypid; 936 } 937 count = (last + 1) * sizeof(pid_t); 938 if (lseek(fd, 0, SEEK_SET) == -1) 939 goto cleanup_count; 940 if (write(fd, pids, count) == -1) 941 goto cleanup_count; 942 (void)ftruncate(fd, count); 943 944 cleanup_count: 945 if (lseek(fd, 0, SEEK_SET) != -1) 946 (void)lockf(fd, F_ULOCK, 0); 947 close(fd); 948 REASSIGN(pids, NULL); 949}
|