blob: e1da651efe03472023342358e13246a207a2eb38 [file] [log] [blame]
Damien Millere4340be2000-09-16 13:29:08 +11001/*
2 * Copyright (c) 2000 Markus Friedl. All rights reserved.
Damien Miller6476cad2005-06-16 13:18:34 +10003 * Copyright (c) 2005 Damien Miller. All rights reserved.
Damien Millere4340be2000-09-16 13:29:08 +11004 *
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
Damien Miller61c51502000-08-18 14:01:04 +100026#include "includes.h"
Damien Miller17e91c02006-03-15 11:28:34 +110027RCSID("$OpenBSD: misc.c,v 1.45 2006/02/10 00:27:13 stevesk Exp $");
28
29#include <sys/ioctl.h>
Damien Miller3a4051e2006-03-15 11:19:42 +110030#include <netinet/tcp.h>
Damien Miller03e20032006-03-15 11:16:59 +110031#ifdef HAVE_PATHS_H
Damien Millera9263d02006-03-15 11:18:26 +110032# include <paths.h>
Damien Miller03e20032006-03-15 11:16:59 +110033#endif
Damien Miller3beb8522006-01-02 23:40:10 +110034#ifdef SSH_TUN_OPENBSD
35#include <net/if.h>
36#endif
Damien Miller61c51502000-08-18 14:01:04 +100037
Kevin Stevesb6e773a2001-02-04 13:20:36 +000038#include "misc.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000039#include "log.h"
Ben Lindstrom06909012001-03-05 06:09:31 +000040#include "xmalloc.h"
Damien Miller61c51502000-08-18 14:01:04 +100041
Ben Lindstrom4cc240d2001-07-04 04:46:56 +000042/* remove newline at end of string */
Damien Miller61c51502000-08-18 14:01:04 +100043char *
44chop(char *s)
45{
46 char *t = s;
47 while (*t) {
Ben Lindstrom1c37c6a2001-12-06 18:00:18 +000048 if (*t == '\n' || *t == '\r') {
Damien Miller61c51502000-08-18 14:01:04 +100049 *t = '\0';
50 return s;
51 }
52 t++;
53 }
54 return s;
55
56}
57
Ben Lindstrom4cc240d2001-07-04 04:46:56 +000058/* set/unset filedescriptor to non-blocking */
Damien Miller232711f2004-06-15 10:35:30 +100059int
Damien Miller61c51502000-08-18 14:01:04 +100060set_nonblock(int fd)
61{
62 int val;
Ben Lindstromc93e84c2001-05-12 00:08:37 +000063
Damien Miller61c51502000-08-18 14:01:04 +100064 val = fcntl(fd, F_GETFL, 0);
65 if (val < 0) {
66 error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
Damien Miller232711f2004-06-15 10:35:30 +100067 return (-1);
Damien Miller61c51502000-08-18 14:01:04 +100068 }
Damien Miller69b69aa2000-10-28 14:19:58 +110069 if (val & O_NONBLOCK) {
Damien Miller232711f2004-06-15 10:35:30 +100070 debug3("fd %d is O_NONBLOCK", fd);
71 return (0);
Damien Miller69b69aa2000-10-28 14:19:58 +110072 }
Damien Milleref095ce2003-05-14 13:41:39 +100073 debug2("fd %d setting O_NONBLOCK", fd);
Damien Miller61c51502000-08-18 14:01:04 +100074 val |= O_NONBLOCK;
Damien Miller232711f2004-06-15 10:35:30 +100075 if (fcntl(fd, F_SETFL, val) == -1) {
76 debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd,
77 strerror(errno));
78 return (-1);
79 }
80 return (0);
Damien Miller61c51502000-08-18 14:01:04 +100081}
82
Damien Miller232711f2004-06-15 10:35:30 +100083int
Ben Lindstromc93e84c2001-05-12 00:08:37 +000084unset_nonblock(int fd)
85{
86 int val;
87
88 val = fcntl(fd, F_GETFL, 0);
89 if (val < 0) {
90 error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
Damien Miller232711f2004-06-15 10:35:30 +100091 return (-1);
Ben Lindstromc93e84c2001-05-12 00:08:37 +000092 }
93 if (!(val & O_NONBLOCK)) {
Damien Miller232711f2004-06-15 10:35:30 +100094 debug3("fd %d is not O_NONBLOCK", fd);
95 return (0);
Ben Lindstromc93e84c2001-05-12 00:08:37 +000096 }
Ben Lindstrom352b1c22001-06-21 03:04:37 +000097 debug("fd %d clearing O_NONBLOCK", fd);
Ben Lindstromc93e84c2001-05-12 00:08:37 +000098 val &= ~O_NONBLOCK;
Damien Miller232711f2004-06-15 10:35:30 +100099 if (fcntl(fd, F_SETFL, val) == -1) {
100 debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s",
Ben Lindstrom84fcb312002-03-05 01:48:09 +0000101 fd, strerror(errno));
Damien Miller232711f2004-06-15 10:35:30 +1000102 return (-1);
103 }
104 return (0);
Ben Lindstromc93e84c2001-05-12 00:08:37 +0000105}
106
Damien Miller398e1cf2002-02-05 11:52:13 +1100107/* disable nagle on socket */
108void
109set_nodelay(int fd)
110{
Ben Lindstrome86de512002-03-05 01:28:14 +0000111 int opt;
112 socklen_t optlen;
Damien Miller398e1cf2002-02-05 11:52:13 +1100113
Ben Lindstrom1ebd7a52002-02-26 18:12:51 +0000114 optlen = sizeof opt;
115 if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) {
Darren Tucker6db8f932003-11-03 20:07:14 +1100116 debug("getsockopt TCP_NODELAY: %.100s", strerror(errno));
Ben Lindstrom1ebd7a52002-02-26 18:12:51 +0000117 return;
118 }
119 if (opt == 1) {
120 debug2("fd %d is TCP_NODELAY", fd);
121 return;
122 }
123 opt = 1;
Ben Lindstrom1d568f92002-12-23 02:44:36 +0000124 debug2("fd %d setting TCP_NODELAY", fd);
Ben Lindstrom1ebd7a52002-02-26 18:12:51 +0000125 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1)
Damien Miller398e1cf2002-02-05 11:52:13 +1100126 error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
127}
128
Damien Miller61c51502000-08-18 14:01:04 +1000129/* Characters considered whitespace in strsep calls. */
130#define WHITESPACE " \t\r\n"
131
Ben Lindstrom4cc240d2001-07-04 04:46:56 +0000132/* return next token in configuration line */
Damien Miller61c51502000-08-18 14:01:04 +1000133char *
134strdelim(char **s)
135{
136 char *old;
137 int wspace = 0;
138
139 if (*s == NULL)
140 return NULL;
141
142 old = *s;
143
144 *s = strpbrk(*s, WHITESPACE "=");
145 if (*s == NULL)
146 return (old);
147
148 /* Allow only one '=' to be skipped */
149 if (*s[0] == '=')
150 wspace = 1;
151 *s[0] = '\0';
152
153 *s += strspn(*s + 1, WHITESPACE) + 1;
154 if (*s[0] == '=' && !wspace)
155 *s += strspn(*s + 1, WHITESPACE) + 1;
156
157 return (old);
158}
Kevin Stevesb6e773a2001-02-04 13:20:36 +0000159
Ben Lindstrom086cf212001-03-05 05:56:40 +0000160struct passwd *
161pwcopy(struct passwd *pw)
162{
163 struct passwd *copy = xmalloc(sizeof(*copy));
Ben Lindstrom40304422001-03-05 06:22:01 +0000164
Ben Lindstrom086cf212001-03-05 05:56:40 +0000165 memset(copy, 0, sizeof(*copy));
166 copy->pw_name = xstrdup(pw->pw_name);
167 copy->pw_passwd = xstrdup(pw->pw_passwd);
Ben Lindstrom40304422001-03-05 06:22:01 +0000168 copy->pw_gecos = xstrdup(pw->pw_gecos);
Ben Lindstrom086cf212001-03-05 05:56:40 +0000169 copy->pw_uid = pw->pw_uid;
170 copy->pw_gid = pw->pw_gid;
Kevin Steves82456952001-06-22 21:14:18 +0000171#ifdef HAVE_PW_EXPIRE_IN_PASSWD
Ben Lindstrom3af4d462001-06-21 03:11:27 +0000172 copy->pw_expire = pw->pw_expire;
Kevin Steves82456952001-06-22 21:14:18 +0000173#endif
174#ifdef HAVE_PW_CHANGE_IN_PASSWD
Ben Lindstrom3af4d462001-06-21 03:11:27 +0000175 copy->pw_change = pw->pw_change;
Kevin Steves82456952001-06-22 21:14:18 +0000176#endif
Ben Lindstrom0f68db42001-03-05 07:57:09 +0000177#ifdef HAVE_PW_CLASS_IN_PASSWD
Ben Lindstrom086cf212001-03-05 05:56:40 +0000178 copy->pw_class = xstrdup(pw->pw_class);
Ben Lindstrom0f68db42001-03-05 07:57:09 +0000179#endif
Ben Lindstrom086cf212001-03-05 05:56:40 +0000180 copy->pw_dir = xstrdup(pw->pw_dir);
181 copy->pw_shell = xstrdup(pw->pw_shell);
182 return copy;
183}
184
Ben Lindstrom4cc240d2001-07-04 04:46:56 +0000185/*
186 * Convert ASCII string to TCP/IP port number.
187 * Port must be >0 and <=65535.
188 * Return 0 if invalid.
189 */
190int
191a2port(const char *s)
Ben Lindstrom19066a12001-04-12 23:39:26 +0000192{
193 long port;
194 char *endp;
195
196 errno = 0;
197 port = strtol(s, &endp, 0);
198 if (s == endp || *endp != '\0' ||
199 (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) ||
200 port <= 0 || port > 65535)
201 return 0;
202
203 return port;
204}
205
Damien Millerd27b9472005-12-13 19:29:02 +1100206int
207a2tun(const char *s, int *remote)
208{
209 const char *errstr = NULL;
210 char *sp, *ep;
211 int tun;
212
213 if (remote != NULL) {
Damien Miller7b58e802005-12-13 19:33:19 +1100214 *remote = SSH_TUNID_ANY;
Damien Millerd27b9472005-12-13 19:29:02 +1100215 sp = xstrdup(s);
216 if ((ep = strchr(sp, ':')) == NULL) {
217 xfree(sp);
218 return (a2tun(s, NULL));
219 }
220 ep[0] = '\0'; ep++;
221 *remote = a2tun(ep, NULL);
222 tun = a2tun(sp, NULL);
223 xfree(sp);
Damien Miller7b58e802005-12-13 19:33:19 +1100224 return (*remote == SSH_TUNID_ERR ? *remote : tun);
Damien Millerd27b9472005-12-13 19:29:02 +1100225 }
226
227 if (strcasecmp(s, "any") == 0)
Damien Miller7b58e802005-12-13 19:33:19 +1100228 return (SSH_TUNID_ANY);
Damien Millerd27b9472005-12-13 19:29:02 +1100229
Damien Miller7b58e802005-12-13 19:33:19 +1100230 tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr);
231 if (errstr != NULL)
232 return (SSH_TUNID_ERR);
Damien Millerd27b9472005-12-13 19:29:02 +1100233
234 return (tun);
235}
236
Ben Lindstrom1bda4c82001-06-05 19:59:08 +0000237#define SECONDS 1
238#define MINUTES (SECONDS * 60)
239#define HOURS (MINUTES * 60)
240#define DAYS (HOURS * 24)
241#define WEEKS (DAYS * 7)
242
Ben Lindstrom4cc240d2001-07-04 04:46:56 +0000243/*
244 * Convert a time string into seconds; format is
245 * a sequence of:
246 * time[qualifier]
247 *
248 * Valid time qualifiers are:
249 * <none> seconds
250 * s|S seconds
251 * m|M minutes
252 * h|H hours
253 * d|D days
254 * w|W weeks
255 *
256 * Examples:
257 * 90m 90 minutes
258 * 1h30m 90 minutes
259 * 2d 2 days
260 * 1w 1 week
261 *
262 * Return -1 if time string is invalid.
263 */
264long
265convtime(const char *s)
Ben Lindstrom1bda4c82001-06-05 19:59:08 +0000266{
267 long total, secs;
268 const char *p;
269 char *endp;
270
271 errno = 0;
272 total = 0;
273 p = s;
274
275 if (p == NULL || *p == '\0')
276 return -1;
277
278 while (*p) {
279 secs = strtol(p, &endp, 10);
280 if (p == endp ||
281 (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) ||
282 secs < 0)
283 return -1;
284
285 switch (*endp++) {
286 case '\0':
287 endp--;
288 case 's':
289 case 'S':
290 break;
291 case 'm':
292 case 'M':
293 secs *= MINUTES;
294 break;
295 case 'h':
296 case 'H':
297 secs *= HOURS;
298 break;
299 case 'd':
300 case 'D':
301 secs *= DAYS;
302 break;
303 case 'w':
304 case 'W':
305 secs *= WEEKS;
306 break;
307 default:
308 return -1;
309 }
310 total += secs;
311 if (total < 0)
312 return -1;
313 p = endp;
314 }
315
316 return total;
317}
318
Damien Millerf91ee4c2005-03-01 21:24:33 +1100319/*
320 * Search for next delimiter between hostnames/addresses and ports.
321 * Argument may be modified (for termination).
322 * Returns *cp if parsing succeeds.
323 * *cp is set to the start of the next delimiter, if one was found.
324 * If this is the last field, *cp is set to NULL.
325 */
326char *
327hpdelim(char **cp)
328{
329 char *s, *old;
330
331 if (cp == NULL || *cp == NULL)
332 return NULL;
333
334 old = s = *cp;
335 if (*s == '[') {
336 if ((s = strchr(s, ']')) == NULL)
337 return NULL;
338 else
339 s++;
340 } else if ((s = strpbrk(s, ":/")) == NULL)
341 s = *cp + strlen(*cp); /* skip to end (see first case below) */
342
343 switch (*s) {
344 case '\0':
345 *cp = NULL; /* no more fields*/
346 break;
Darren Tucker47eede72005-03-14 23:08:12 +1100347
Damien Millerf91ee4c2005-03-01 21:24:33 +1100348 case ':':
349 case '/':
350 *s = '\0'; /* terminate */
351 *cp = s + 1;
352 break;
Darren Tucker47eede72005-03-14 23:08:12 +1100353
Damien Millerf91ee4c2005-03-01 21:24:33 +1100354 default:
355 return NULL;
356 }
357
358 return old;
359}
360
Ben Lindstrom4529b702001-05-03 23:39:53 +0000361char *
362cleanhostname(char *host)
363{
364 if (*host == '[' && host[strlen(host) - 1] == ']') {
365 host[strlen(host) - 1] = '\0';
366 return (host + 1);
367 } else
368 return host;
369}
370
371char *
372colon(char *cp)
373{
374 int flag = 0;
375
376 if (*cp == ':') /* Leading colon is part of file name. */
377 return (0);
378 if (*cp == '[')
379 flag = 1;
380
381 for (; *cp; ++cp) {
382 if (*cp == '@' && *(cp+1) == '[')
383 flag = 1;
384 if (*cp == ']' && *(cp+1) == ':' && flag)
385 return (cp+1);
386 if (*cp == ':' && !flag)
387 return (cp);
388 if (*cp == '/')
389 return (0);
390 }
391 return (0);
392}
393
Ben Lindstrom4cc240d2001-07-04 04:46:56 +0000394/* function to assist building execv() arguments */
Ben Lindstrom387c4722001-05-08 20:27:25 +0000395void
396addargs(arglist *args, char *fmt, ...)
397{
398 va_list ap;
Damien Miller3eec6b72006-01-31 21:49:27 +1100399 char *cp;
Darren Tuckerc7a6fc42004-08-13 21:18:00 +1000400 u_int nalloc;
Damien Miller3eec6b72006-01-31 21:49:27 +1100401 int r;
Ben Lindstrom387c4722001-05-08 20:27:25 +0000402
403 va_start(ap, fmt);
Damien Miller3eec6b72006-01-31 21:49:27 +1100404 r = vasprintf(&cp, fmt, ap);
Ben Lindstrom387c4722001-05-08 20:27:25 +0000405 va_end(ap);
Damien Miller3eec6b72006-01-31 21:49:27 +1100406 if (r == -1)
407 fatal("addargs: argument too long");
Ben Lindstrom387c4722001-05-08 20:27:25 +0000408
Darren Tuckerfb16b242003-09-22 21:04:23 +1000409 nalloc = args->nalloc;
Ben Lindstrom387c4722001-05-08 20:27:25 +0000410 if (args->list == NULL) {
Darren Tuckerfb16b242003-09-22 21:04:23 +1000411 nalloc = 32;
Ben Lindstrom387c4722001-05-08 20:27:25 +0000412 args->num = 0;
Darren Tuckerfb16b242003-09-22 21:04:23 +1000413 } else if (args->num+2 >= nalloc)
414 nalloc *= 2;
Ben Lindstrom387c4722001-05-08 20:27:25 +0000415
Darren Tuckerfb16b242003-09-22 21:04:23 +1000416 args->list = xrealloc(args->list, nalloc * sizeof(char *));
417 args->nalloc = nalloc;
Damien Miller3eec6b72006-01-31 21:49:27 +1100418 args->list[args->num++] = cp;
Ben Lindstrom387c4722001-05-08 20:27:25 +0000419 args->list[args->num] = NULL;
420}
Darren Tucker22cc7412004-12-06 22:47:41 +1100421
Damien Miller3eec6b72006-01-31 21:49:27 +1100422void
423replacearg(arglist *args, u_int which, char *fmt, ...)
424{
425 va_list ap;
426 char *cp;
427 int r;
428
429 va_start(ap, fmt);
430 r = vasprintf(&cp, fmt, ap);
431 va_end(ap);
432 if (r == -1)
433 fatal("replacearg: argument too long");
434
435 if (which >= args->num)
436 fatal("replacearg: tried to replace invalid arg %d >= %d",
437 which, args->num);
438 xfree(args->list[which]);
439 args->list[which] = cp;
440}
441
442void
443freeargs(arglist *args)
444{
445 u_int i;
446
447 if (args->list != NULL) {
448 for (i = 0; i < args->num; i++)
449 xfree(args->list[i]);
450 xfree(args->list);
451 args->nalloc = args->num = 0;
452 args->list = NULL;
453 }
454}
455
Darren Tucker22cc7412004-12-06 22:47:41 +1100456/*
Damien Miller5fd38c02005-05-26 12:02:14 +1000457 * Expands tildes in the file name. Returns data allocated by xmalloc.
458 * Warning: this calls getpw*.
459 */
460char *
461tilde_expand_filename(const char *filename, uid_t uid)
462{
463 const char *path;
464 char user[128], ret[MAXPATHLEN];
465 struct passwd *pw;
Damien Millereccb9de2005-06-17 12:59:34 +1000466 u_int len, slash;
Damien Miller5fd38c02005-05-26 12:02:14 +1000467
468 if (*filename != '~')
469 return (xstrdup(filename));
470 filename++;
471
472 path = strchr(filename, '/');
473 if (path != NULL && path > filename) { /* ~user/path */
Damien Millereccb9de2005-06-17 12:59:34 +1000474 slash = path - filename;
475 if (slash > sizeof(user) - 1)
Damien Miller5fd38c02005-05-26 12:02:14 +1000476 fatal("tilde_expand_filename: ~username too long");
Damien Millereccb9de2005-06-17 12:59:34 +1000477 memcpy(user, filename, slash);
478 user[slash] = '\0';
Damien Miller5fd38c02005-05-26 12:02:14 +1000479 if ((pw = getpwnam(user)) == NULL)
480 fatal("tilde_expand_filename: No such user %s", user);
481 } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */
482 fatal("tilde_expand_filename: No such uid %d", uid);
483
484 if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret))
485 fatal("tilde_expand_filename: Path too long");
486
487 /* Make sure directory has a trailing '/' */
488 len = strlen(pw->pw_dir);
489 if ((len == 0 || pw->pw_dir[len - 1] != '/') &&
490 strlcat(ret, "/", sizeof(ret)) >= sizeof(ret))
491 fatal("tilde_expand_filename: Path too long");
492
493 /* Skip leading '/' from specified path */
494 if (path != NULL)
495 filename = path + 1;
496 if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret))
497 fatal("tilde_expand_filename: Path too long");
498
499 return (xstrdup(ret));
500}
501
502/*
Damien Miller6476cad2005-06-16 13:18:34 +1000503 * Expand a string with a set of %[char] escapes. A number of escapes may be
504 * specified as (char *escape_chars, char *replacement) pairs. The list must
Darren Tuckerbee73d52005-07-14 17:05:02 +1000505 * be terminated by a NULL escape_char. Returns replaced string in memory
Damien Miller6476cad2005-06-16 13:18:34 +1000506 * allocated by xmalloc.
507 */
508char *
509percent_expand(const char *string, ...)
510{
511#define EXPAND_MAX_KEYS 16
512 struct {
513 const char *key;
514 const char *repl;
515 } keys[EXPAND_MAX_KEYS];
Damien Millereccb9de2005-06-17 12:59:34 +1000516 u_int num_keys, i, j;
Damien Miller6476cad2005-06-16 13:18:34 +1000517 char buf[4096];
518 va_list ap;
519
520 /* Gather keys */
521 va_start(ap, string);
522 for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) {
523 keys[num_keys].key = va_arg(ap, char *);
524 if (keys[num_keys].key == NULL)
525 break;
526 keys[num_keys].repl = va_arg(ap, char *);
527 if (keys[num_keys].repl == NULL)
528 fatal("percent_expand: NULL replacement");
529 }
530 va_end(ap);
531
532 if (num_keys >= EXPAND_MAX_KEYS)
533 fatal("percent_expand: too many keys");
534
535 /* Expand string */
536 *buf = '\0';
537 for (i = 0; *string != '\0'; string++) {
538 if (*string != '%') {
539 append:
540 buf[i++] = *string;
541 if (i >= sizeof(buf))
542 fatal("percent_expand: string too long");
543 buf[i] = '\0';
544 continue;
545 }
546 string++;
547 if (*string == '%')
548 goto append;
549 for (j = 0; j < num_keys; j++) {
550 if (strchr(keys[j].key, *string) != NULL) {
551 i = strlcat(buf, keys[j].repl, sizeof(buf));
552 if (i >= sizeof(buf))
553 fatal("percent_expand: string too long");
554 break;
555 }
556 }
557 if (j >= num_keys)
558 fatal("percent_expand: unknown key %%%c", *string);
559 }
560 return (xstrdup(buf));
561#undef EXPAND_MAX_KEYS
562}
563
564/*
Darren Tucker22cc7412004-12-06 22:47:41 +1100565 * Read an entire line from a public key file into a static buffer, discarding
566 * lines that exceed the buffer size. Returns 0 on success, -1 on failure.
567 */
568int
569read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
Darren Tuckerf0f90982004-12-11 13:39:50 +1100570 u_long *lineno)
Darren Tucker22cc7412004-12-06 22:47:41 +1100571{
572 while (fgets(buf, bufsz, f) != NULL) {
573 (*lineno)++;
574 if (buf[strlen(buf) - 1] == '\n' || feof(f)) {
575 return 0;
576 } else {
Darren Tuckerf0f90982004-12-11 13:39:50 +1100577 debug("%s: %s line %lu exceeds size limit", __func__,
578 filename, *lineno);
Darren Tucker22cc7412004-12-06 22:47:41 +1100579 /* discard remainder of line */
Darren Tucker47eede72005-03-14 23:08:12 +1100580 while (fgetc(f) != '\n' && !feof(f))
Darren Tucker22cc7412004-12-06 22:47:41 +1100581 ; /* nothing */
582 }
583 }
584 return -1;
585}
Damien Miller13390022005-07-06 09:44:19 +1000586
Damien Millerd27b9472005-12-13 19:29:02 +1100587int
Damien Miller7b58e802005-12-13 19:33:19 +1100588tun_open(int tun, int mode)
Damien Millerd27b9472005-12-13 19:29:02 +1100589{
Damien Miller62a31c92005-12-13 20:44:13 +1100590#if defined(CUSTOM_SYS_TUN_OPEN)
591 return (sys_tun_open(tun, mode));
Damien Miller2dcddbf2006-01-01 19:47:05 +1100592#elif defined(SSH_TUN_OPENBSD)
Damien Miller7b58e802005-12-13 19:33:19 +1100593 struct ifreq ifr;
Damien Millerd27b9472005-12-13 19:29:02 +1100594 char name[100];
Damien Miller7b58e802005-12-13 19:33:19 +1100595 int fd = -1, sock;
Damien Millerd27b9472005-12-13 19:29:02 +1100596
Damien Miller7b58e802005-12-13 19:33:19 +1100597 /* Open the tunnel device */
598 if (tun <= SSH_TUNID_MAX) {
Damien Millerd27b9472005-12-13 19:29:02 +1100599 snprintf(name, sizeof(name), "/dev/tun%d", tun);
Damien Miller7b58e802005-12-13 19:33:19 +1100600 fd = open(name, O_RDWR);
601 } else if (tun == SSH_TUNID_ANY) {
602 for (tun = 100; tun >= 0; tun--) {
603 snprintf(name, sizeof(name), "/dev/tun%d", tun);
604 if ((fd = open(name, O_RDWR)) >= 0)
605 break;
Damien Millerd27b9472005-12-13 19:29:02 +1100606 }
607 } else {
Damien Millera210d522006-01-02 23:40:30 +1100608 debug("%s: invalid tunnel %u", __func__, tun);
Damien Miller7b58e802005-12-13 19:33:19 +1100609 return (-1);
Damien Millerd27b9472005-12-13 19:29:02 +1100610 }
Damien Miller7b58e802005-12-13 19:33:19 +1100611
612 if (fd < 0) {
613 debug("%s: %s open failed: %s", __func__, name, strerror(errno));
614 return (-1);
615 }
616
617 debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
618
619 /* Set the tunnel device operation mode */
620 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun);
621 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
622 goto failed;
623
624 if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
625 goto failed;
Damien Millera1d9a182006-01-02 23:41:21 +1100626
627 /* Set interface mode */
628 ifr.ifr_flags &= ~IFF_UP;
629 if (mode == SSH_TUNMODE_ETHERNET)
Damien Miller7b58e802005-12-13 19:33:19 +1100630 ifr.ifr_flags |= IFF_LINK0;
Damien Millera1d9a182006-01-02 23:41:21 +1100631 else
632 ifr.ifr_flags &= ~IFF_LINK0;
633 if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
634 goto failed;
635
636 /* Bring interface up */
Damien Miller7b58e802005-12-13 19:33:19 +1100637 ifr.ifr_flags |= IFF_UP;
638 if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
639 goto failed;
640
641 close(sock);
642 return (fd);
643
644 failed:
645 if (fd >= 0)
646 close(fd);
647 if (sock >= 0)
648 close(sock);
649 debug("%s: failed to set %s mode %d: %s", __func__, name,
650 mode, strerror(errno));
Damien Millerd27b9472005-12-13 19:29:02 +1100651 return (-1);
Damien Miller62a31c92005-12-13 20:44:13 +1100652#else
653 error("Tunnel interfaces are not supported on this platform");
654 return (-1);
655#endif
Damien Millerd27b9472005-12-13 19:29:02 +1100656}
657
Darren Tuckerce321d82005-10-03 18:11:24 +1000658void
659sanitise_stdfd(void)
660{
Damien Miller72c5b7d2006-01-06 14:50:44 +1100661 int nullfd, dupfd;
Darren Tuckerce321d82005-10-03 18:11:24 +1000662
Damien Miller72c5b7d2006-01-06 14:50:44 +1100663 if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
Darren Tuckerce321d82005-10-03 18:11:24 +1000664 fprintf(stderr, "Couldn't open /dev/null: %s", strerror(errno));
665 exit(1);
666 }
Damien Miller72c5b7d2006-01-06 14:50:44 +1100667 while (++dupfd <= 2) {
668 /* Only clobber closed fds */
669 if (fcntl(dupfd, F_GETFL, 0) >= 0)
670 continue;
671 if (dup2(nullfd, dupfd) == -1) {
Darren Tuckerce321d82005-10-03 18:11:24 +1000672 fprintf(stderr, "dup2: %s", strerror(errno));
673 exit(1);
674 }
Darren Tuckerce321d82005-10-03 18:11:24 +1000675 }
676 if (nullfd > 2)
677 close(nullfd);
678}
679
Damien Miller13390022005-07-06 09:44:19 +1000680char *
681tohex(const u_char *d, u_int l)
682{
683 char b[3], *r;
684 u_int i, hl;
685
686 hl = l * 2 + 1;
687 r = xmalloc(hl);
688 *r = '\0';
689 for (i = 0; i < l; i++) {
690 snprintf(b, sizeof(b), "%02x", d[i]);
691 strlcat(r, b, hl);
692 }
693 return (r);
694}
695