1 /* 2 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2005 Damien Miller. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "includes.h" 27 RCSID("$OpenBSD: misc.c,v 1.34 2005/07/08 09:26:18 dtucker Exp $"); 28 29 #include "misc.h" 30 #include "log.h" 31 #include "xmalloc.h" 32 33 /* remove newline at end of string */ 34 char * 35 chop(char *s) 36 { 37 char *t = s; 38 while (*t) { 39 if (*t == '\n' || *t == '\r') { 40 *t = '\0'; 41 return s; 42 } 43 t++; 44 } 45 return s; 46 47 } 48 49 /* set/unset filedescriptor to non-blocking */ 50 int 51 set_nonblock(int fd) 52 { 53 int val; 54 55 val = fcntl(fd, F_GETFL, 0); 56 if (val < 0) { 57 error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 58 return (-1); 59 } 60 if (val & O_NONBLOCK) { 61 debug3("fd %d is O_NONBLOCK", fd); 62 return (0); 63 } 64 debug2("fd %d setting O_NONBLOCK", fd); 65 val |= O_NONBLOCK; 66 if (fcntl(fd, F_SETFL, val) == -1) { 67 debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, 68 strerror(errno)); 69 return (-1); 70 } 71 return (0); 72 } 73 74 int 75 unset_nonblock(int fd) 76 { 77 int val; 78 79 val = fcntl(fd, F_GETFL, 0); 80 if (val < 0) { 81 error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 82 return (-1); 83 } 84 if (!(val & O_NONBLOCK)) { 85 debug3("fd %d is not O_NONBLOCK", fd); 86 return (0); 87 } 88 debug("fd %d clearing O_NONBLOCK", fd); 89 val &= ~O_NONBLOCK; 90 if (fcntl(fd, F_SETFL, val) == -1) { 91 debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", 92 fd, strerror(errno)); 93 return (-1); 94 } 95 return (0); 96 } 97 98 /* disable nagle on socket */ 99 void 100 set_nodelay(int fd) 101 { 102 int opt; 103 socklen_t optlen; 104 105 optlen = sizeof opt; 106 if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { 107 debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); 108 return; 109 } 110 if (opt == 1) { 111 debug2("fd %d is TCP_NODELAY", fd); 112 return; 113 } 114 opt = 1; 115 debug2("fd %d setting TCP_NODELAY", fd); 116 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) 117 error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); 118 } 119 120 /* Characters considered whitespace in strsep calls. */ 121 #define WHITESPACE " \t\r\n" 122 123 /* return next token in configuration line */ 124 char * 125 strdelim(char **s) 126 { 127 char *old; 128 int wspace = 0; 129 130 if (*s == NULL) 131 return NULL; 132 133 old = *s; 134 135 *s = strpbrk(*s, WHITESPACE "="); 136 if (*s == NULL) 137 return (old); 138 139 /* Allow only one '=' to be skipped */ 140 if (*s[0] == '=') 141 wspace = 1; 142 *s[0] = '\0'; 143 144 *s += strspn(*s + 1, WHITESPACE) + 1; 145 if (*s[0] == '=' && !wspace) 146 *s += strspn(*s + 1, WHITESPACE) + 1; 147 148 return (old); 149 } 150 151 struct passwd * 152 pwcopy(struct passwd *pw) 153 { 154 struct passwd *copy = xmalloc(sizeof(*copy)); 155 156 memset(copy, 0, sizeof(*copy)); 157 copy->pw_name = xstrdup(pw->pw_name); 158 copy->pw_passwd = xstrdup(pw->pw_passwd); 159 copy->pw_gecos = xstrdup(pw->pw_gecos); 160 copy->pw_uid = pw->pw_uid; 161 copy->pw_gid = pw->pw_gid; 162 #ifdef HAVE_PW_EXPIRE_IN_PASSWD 163 copy->pw_expire = pw->pw_expire; 164 #endif 165 #ifdef HAVE_PW_CHANGE_IN_PASSWD 166 copy->pw_change = pw->pw_change; 167 #endif 168 #ifdef HAVE_PW_CLASS_IN_PASSWD 169 copy->pw_class = xstrdup(pw->pw_class); 170 #endif 171 copy->pw_dir = xstrdup(pw->pw_dir); 172 copy->pw_shell = xstrdup(pw->pw_shell); 173 return copy; 174 } 175 176 /* 177 * Convert ASCII string to TCP/IP port number. 178 * Port must be >0 and <=65535. 179 * Return 0 if invalid. 180 */ 181 int 182 a2port(const char *s) 183 { 184 long port; 185 char *endp; 186 187 errno = 0; 188 port = strtol(s, &endp, 0); 189 if (s == endp || *endp != '\0' || 190 (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) || 191 port <= 0 || port > 65535) 192 return 0; 193 194 return port; 195 } 196 197 #define SECONDS 1 198 #define MINUTES (SECONDS * 60) 199 #define HOURS (MINUTES * 60) 200 #define DAYS (HOURS * 24) 201 #define WEEKS (DAYS * 7) 202 203 /* 204 * Convert a time string into seconds; format is 205 * a sequence of: 206 * time[qualifier] 207 * 208 * Valid time qualifiers are: 209 * <none> seconds 210 * s|S seconds 211 * m|M minutes 212 * h|H hours 213 * d|D days 214 * w|W weeks 215 * 216 * Examples: 217 * 90m 90 minutes 218 * 1h30m 90 minutes 219 * 2d 2 days 220 * 1w 1 week 221 * 222 * Return -1 if time string is invalid. 223 */ 224 long 225 convtime(const char *s) 226 { 227 long total, secs; 228 const char *p; 229 char *endp; 230 231 errno = 0; 232 total = 0; 233 p = s; 234 235 if (p == NULL || *p == '\0') 236 return -1; 237 238 while (*p) { 239 secs = strtol(p, &endp, 10); 240 if (p == endp || 241 (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 242 secs < 0) 243 return -1; 244 245 switch (*endp++) { 246 case '\0': 247 endp--; 248 case 's': 249 case 'S': 250 break; 251 case 'm': 252 case 'M': 253 secs *= MINUTES; 254 break; 255 case 'h': 256 case 'H': 257 secs *= HOURS; 258 break; 259 case 'd': 260 case 'D': 261 secs *= DAYS; 262 break; 263 case 'w': 264 case 'W': 265 secs *= WEEKS; 266 break; 267 default: 268 return -1; 269 } 270 total += secs; 271 if (total < 0) 272 return -1; 273 p = endp; 274 } 275 276 return total; 277 } 278 279 /* 280 * Search for next delimiter between hostnames/addresses and ports. 281 * Argument may be modified (for termination). 282 * Returns *cp if parsing succeeds. 283 * *cp is set to the start of the next delimiter, if one was found. 284 * If this is the last field, *cp is set to NULL. 285 */ 286 char * 287 hpdelim(char **cp) 288 { 289 char *s, *old; 290 291 if (cp == NULL || *cp == NULL) 292 return NULL; 293 294 old = s = *cp; 295 if (*s == '[') { 296 if ((s = strchr(s, ']')) == NULL) 297 return NULL; 298 else 299 s++; 300 } else if ((s = strpbrk(s, ":/")) == NULL) 301 s = *cp + strlen(*cp); /* skip to end (see first case below) */ 302 303 switch (*s) { 304 case '\0': 305 *cp = NULL; /* no more fields*/ 306 break; 307 308 case ':': 309 case '/': 310 *s = '\0'; /* terminate */ 311 *cp = s + 1; 312 break; 313 314 default: 315 return NULL; 316 } 317 318 return old; 319 } 320 321 char * 322 cleanhostname(char *host) 323 { 324 if (*host == '[' && host[strlen(host) - 1] == ']') { 325 host[strlen(host) - 1] = '\0'; 326 return (host + 1); 327 } else 328 return host; 329 } 330 331 char * 332 colon(char *cp) 333 { 334 int flag = 0; 335 336 if (*cp == ':') /* Leading colon is part of file name. */ 337 return (0); 338 if (*cp == '[') 339 flag = 1; 340 341 for (; *cp; ++cp) { 342 if (*cp == '@' && *(cp+1) == '[') 343 flag = 1; 344 if (*cp == ']' && *(cp+1) == ':' && flag) 345 return (cp+1); 346 if (*cp == ':' && !flag) 347 return (cp); 348 if (*cp == '/') 349 return (0); 350 } 351 return (0); 352 } 353 354 /* function to assist building execv() arguments */ 355 void 356 addargs(arglist *args, char *fmt, ...) 357 { 358 va_list ap; 359 char buf[1024]; 360 u_int nalloc; 361 362 va_start(ap, fmt); 363 vsnprintf(buf, sizeof(buf), fmt, ap); 364 va_end(ap); 365 366 nalloc = args->nalloc; 367 if (args->list == NULL) { 368 nalloc = 32; 369 args->num = 0; 370 } else if (args->num+2 >= nalloc) 371 nalloc *= 2; 372 373 args->list = xrealloc(args->list, nalloc * sizeof(char *)); 374 args->nalloc = nalloc; 375 args->list[args->num++] = xstrdup(buf); 376 args->list[args->num] = NULL; 377 } 378 379 /* 380 * Expands tildes in the file name. Returns data allocated by xmalloc. 381 * Warning: this calls getpw*. 382 */ 383 char * 384 tilde_expand_filename(const char *filename, uid_t uid) 385 { 386 const char *path; 387 char user[128], ret[MAXPATHLEN]; 388 struct passwd *pw; 389 u_int len, slash; 390 391 if (*filename != '~') 392 return (xstrdup(filename)); 393 filename++; 394 395 path = strchr(filename, '/'); 396 if (path != NULL && path > filename) { /* ~user/path */ 397 slash = path - filename; 398 if (slash > sizeof(user) - 1) 399 fatal("tilde_expand_filename: ~username too long"); 400 memcpy(user, filename, slash); 401 user[slash] = '\0'; 402 if ((pw = getpwnam(user)) == NULL) 403 fatal("tilde_expand_filename: No such user %s", user); 404 } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ 405 fatal("tilde_expand_filename: No such uid %d", uid); 406 407 if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret)) 408 fatal("tilde_expand_filename: Path too long"); 409 410 /* Make sure directory has a trailing '/' */ 411 len = strlen(pw->pw_dir); 412 if ((len == 0 || pw->pw_dir[len - 1] != '/') && 413 strlcat(ret, "/", sizeof(ret)) >= sizeof(ret)) 414 fatal("tilde_expand_filename: Path too long"); 415 416 /* Skip leading '/' from specified path */ 417 if (path != NULL) 418 filename = path + 1; 419 if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret)) 420 fatal("tilde_expand_filename: Path too long"); 421 422 return (xstrdup(ret)); 423 } 424 425 /* 426 * Expand a string with a set of %[char] escapes. A number of escapes may be 427 * specified as (char *escape_chars, char *replacement) pairs. The list must 428 * be terminated by a NULL escape_char. Returns replaced string in memory 429 * allocated by xmalloc. 430 */ 431 char * 432 percent_expand(const char *string, ...) 433 { 434 #define EXPAND_MAX_KEYS 16 435 struct { 436 const char *key; 437 const char *repl; 438 } keys[EXPAND_MAX_KEYS]; 439 u_int num_keys, i, j; 440 char buf[4096]; 441 va_list ap; 442 443 /* Gather keys */ 444 va_start(ap, string); 445 for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { 446 keys[num_keys].key = va_arg(ap, char *); 447 if (keys[num_keys].key == NULL) 448 break; 449 keys[num_keys].repl = va_arg(ap, char *); 450 if (keys[num_keys].repl == NULL) 451 fatal("percent_expand: NULL replacement"); 452 } 453 va_end(ap); 454 455 if (num_keys >= EXPAND_MAX_KEYS) 456 fatal("percent_expand: too many keys"); 457 458 /* Expand string */ 459 *buf = '\0'; 460 for (i = 0; *string != '\0'; string++) { 461 if (*string != '%') { 462 append: 463 buf[i++] = *string; 464 if (i >= sizeof(buf)) 465 fatal("percent_expand: string too long"); 466 buf[i] = '\0'; 467 continue; 468 } 469 string++; 470 if (*string == '%') 471 goto append; 472 for (j = 0; j < num_keys; j++) { 473 if (strchr(keys[j].key, *string) != NULL) { 474 i = strlcat(buf, keys[j].repl, sizeof(buf)); 475 if (i >= sizeof(buf)) 476 fatal("percent_expand: string too long"); 477 break; 478 } 479 } 480 if (j >= num_keys) 481 fatal("percent_expand: unknown key %%%c", *string); 482 } 483 return (xstrdup(buf)); 484 #undef EXPAND_MAX_KEYS 485 } 486 487 /* 488 * Read an entire line from a public key file into a static buffer, discarding 489 * lines that exceed the buffer size. Returns 0 on success, -1 on failure. 490 */ 491 int 492 read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, 493 u_long *lineno) 494 { 495 while (fgets(buf, bufsz, f) != NULL) { 496 (*lineno)++; 497 if (buf[strlen(buf) - 1] == '\n' || feof(f)) { 498 return 0; 499 } else { 500 debug("%s: %s line %lu exceeds size limit", __func__, 501 filename, *lineno); 502 /* discard remainder of line */ 503 while (fgetc(f) != '\n' && !feof(f)) 504 ; /* nothing */ 505 } 506 } 507 return -1; 508 } 509 510 char * 511 tohex(const u_char *d, u_int l) 512 { 513 char b[3], *r; 514 u_int i, hl; 515 516 hl = l * 2 + 1; 517 r = xmalloc(hl); 518 *r = '\0'; 519 for (i = 0; i < l; i++) { 520 snprintf(b, sizeof(b), "%02x", d[i]); 521 strlcat(r, b, hl); 522 } 523 return (r); 524 } 525 526