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