]> andersk Git - openssh.git/blob - misc.c
- djm@cvs.openbsd.org 2006/03/25 00:05:41
[openssh.git] / misc.c
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
28 #include <sys/ioctl.h>
29 #include <netinet/tcp.h>
30 #ifdef HAVE_PATHS_H
31 # include <paths.h>
32 #endif
33 #ifdef SSH_TUN_OPENBSD
34 #include <net/if.h>
35 #endif
36
37 #include "misc.h"
38 #include "log.h"
39 #include "xmalloc.h"
40
41 /* remove newline at end of string */
42 char *
43 chop(char *s)
44 {
45         char *t = s;
46         while (*t) {
47                 if (*t == '\n' || *t == '\r') {
48                         *t = '\0';
49                         return s;
50                 }
51                 t++;
52         }
53         return s;
54
55 }
56
57 /* set/unset filedescriptor to non-blocking */
58 int
59 set_nonblock(int fd)
60 {
61         int val;
62
63         val = fcntl(fd, F_GETFL, 0);
64         if (val < 0) {
65                 error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
66                 return (-1);
67         }
68         if (val & O_NONBLOCK) {
69                 debug3("fd %d is O_NONBLOCK", fd);
70                 return (0);
71         }
72         debug2("fd %d setting O_NONBLOCK", fd);
73         val |= O_NONBLOCK;
74         if (fcntl(fd, F_SETFL, val) == -1) {
75                 debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd,
76                     strerror(errno));
77                 return (-1);
78         }
79         return (0);
80 }
81
82 int
83 unset_nonblock(int fd)
84 {
85         int val;
86
87         val = fcntl(fd, F_GETFL, 0);
88         if (val < 0) {
89                 error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
90                 return (-1);
91         }
92         if (!(val & O_NONBLOCK)) {
93                 debug3("fd %d is not O_NONBLOCK", fd);
94                 return (0);
95         }
96         debug("fd %d clearing O_NONBLOCK", fd);
97         val &= ~O_NONBLOCK;
98         if (fcntl(fd, F_SETFL, val) == -1) {
99                 debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s",
100                     fd, strerror(errno));
101                 return (-1);
102         }
103         return (0);
104 }
105
106 /* disable nagle on socket */
107 void
108 set_nodelay(int fd)
109 {
110         int opt;
111         socklen_t optlen;
112
113         optlen = sizeof opt;
114         if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) {
115                 debug("getsockopt TCP_NODELAY: %.100s", strerror(errno));
116                 return;
117         }
118         if (opt == 1) {
119                 debug2("fd %d is TCP_NODELAY", fd);
120                 return;
121         }
122         opt = 1;
123         debug2("fd %d setting TCP_NODELAY", fd);
124         if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1)
125                 error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
126 }
127
128 /* Characters considered whitespace in strsep calls. */
129 #define WHITESPACE " \t\r\n"
130 #define QUOTE   "\""
131
132 /* return next token in configuration line */
133 char *
134 strdelim(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 QUOTE "=");
145         if (*s == NULL)
146                 return (old);
147
148         if (*s[0] == '\"') {
149                 memmove(*s, *s + 1, strlen(*s)); /* move nul too */
150                 /* Find matching quote */
151                 if ((*s = strpbrk(*s, QUOTE)) == NULL) {
152                         return (NULL);          /* no matching quote */
153                 } else {
154                         *s[0] = '\0';
155                         return (old);
156                 }
157         }
158
159         /* Allow only one '=' to be skipped */
160         if (*s[0] == '=')
161                 wspace = 1;
162         *s[0] = '\0';
163
164         /* Skip any extra whitespace after first token */
165         *s += strspn(*s + 1, WHITESPACE) + 1;
166         if (*s[0] == '=' && !wspace)
167                 *s += strspn(*s + 1, WHITESPACE) + 1;
168
169         return (old);
170 }
171
172 struct passwd *
173 pwcopy(struct passwd *pw)
174 {
175         struct passwd *copy = xcalloc(1, sizeof(*copy));
176
177         copy->pw_name = xstrdup(pw->pw_name);
178         copy->pw_passwd = xstrdup(pw->pw_passwd);
179         copy->pw_gecos = xstrdup(pw->pw_gecos);
180         copy->pw_uid = pw->pw_uid;
181         copy->pw_gid = pw->pw_gid;
182 #ifdef HAVE_PW_EXPIRE_IN_PASSWD
183         copy->pw_expire = pw->pw_expire;
184 #endif
185 #ifdef HAVE_PW_CHANGE_IN_PASSWD
186         copy->pw_change = pw->pw_change;
187 #endif
188 #ifdef HAVE_PW_CLASS_IN_PASSWD
189         copy->pw_class = xstrdup(pw->pw_class);
190 #endif
191         copy->pw_dir = xstrdup(pw->pw_dir);
192         copy->pw_shell = xstrdup(pw->pw_shell);
193         return copy;
194 }
195
196 /*
197  * Convert ASCII string to TCP/IP port number.
198  * Port must be >0 and <=65535.
199  * Return 0 if invalid.
200  */
201 int
202 a2port(const char *s)
203 {
204         long port;
205         char *endp;
206
207         errno = 0;
208         port = strtol(s, &endp, 0);
209         if (s == endp || *endp != '\0' ||
210             (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) ||
211             port <= 0 || port > 65535)
212                 return 0;
213
214         return port;
215 }
216
217 int
218 a2tun(const char *s, int *remote)
219 {
220         const char *errstr = NULL;
221         char *sp, *ep;
222         int tun;
223
224         if (remote != NULL) {
225                 *remote = SSH_TUNID_ANY;
226                 sp = xstrdup(s);
227                 if ((ep = strchr(sp, ':')) == NULL) {
228                         xfree(sp);
229                         return (a2tun(s, NULL));
230                 }
231                 ep[0] = '\0'; ep++;
232                 *remote = a2tun(ep, NULL);
233                 tun = a2tun(sp, NULL);
234                 xfree(sp);
235                 return (*remote == SSH_TUNID_ERR ? *remote : tun);
236         }
237
238         if (strcasecmp(s, "any") == 0)
239                 return (SSH_TUNID_ANY);
240
241         tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr);
242         if (errstr != NULL)
243                 return (SSH_TUNID_ERR);
244
245         return (tun);
246 }
247
248 #define SECONDS         1
249 #define MINUTES         (SECONDS * 60)
250 #define HOURS           (MINUTES * 60)
251 #define DAYS            (HOURS * 24)
252 #define WEEKS           (DAYS * 7)
253
254 /*
255  * Convert a time string into seconds; format is
256  * a sequence of:
257  *      time[qualifier]
258  *
259  * Valid time qualifiers are:
260  *      <none>  seconds
261  *      s|S     seconds
262  *      m|M     minutes
263  *      h|H     hours
264  *      d|D     days
265  *      w|W     weeks
266  *
267  * Examples:
268  *      90m     90 minutes
269  *      1h30m   90 minutes
270  *      2d      2 days
271  *      1w      1 week
272  *
273  * Return -1 if time string is invalid.
274  */
275 long
276 convtime(const char *s)
277 {
278         long total, secs;
279         const char *p;
280         char *endp;
281
282         errno = 0;
283         total = 0;
284         p = s;
285
286         if (p == NULL || *p == '\0')
287                 return -1;
288
289         while (*p) {
290                 secs = strtol(p, &endp, 10);
291                 if (p == endp ||
292                     (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) ||
293                     secs < 0)
294                         return -1;
295
296                 switch (*endp++) {
297                 case '\0':
298                         endp--;
299                         break;
300                 case 's':
301                 case 'S':
302                         break;
303                 case 'm':
304                 case 'M':
305                         secs *= MINUTES;
306                         break;
307                 case 'h':
308                 case 'H':
309                         secs *= HOURS;
310                         break;
311                 case 'd':
312                 case 'D':
313                         secs *= DAYS;
314                         break;
315                 case 'w':
316                 case 'W':
317                         secs *= WEEKS;
318                         break;
319                 default:
320                         return -1;
321                 }
322                 total += secs;
323                 if (total < 0)
324                         return -1;
325                 p = endp;
326         }
327
328         return total;
329 }
330
331 /*
332  * Search for next delimiter between hostnames/addresses and ports.
333  * Argument may be modified (for termination).
334  * Returns *cp if parsing succeeds.
335  * *cp is set to the start of the next delimiter, if one was found.
336  * If this is the last field, *cp is set to NULL.
337  */
338 char *
339 hpdelim(char **cp)
340 {
341         char *s, *old;
342
343         if (cp == NULL || *cp == NULL)
344                 return NULL;
345
346         old = s = *cp;
347         if (*s == '[') {
348                 if ((s = strchr(s, ']')) == NULL)
349                         return NULL;
350                 else
351                         s++;
352         } else if ((s = strpbrk(s, ":/")) == NULL)
353                 s = *cp + strlen(*cp); /* skip to end (see first case below) */
354
355         switch (*s) {
356         case '\0':
357                 *cp = NULL;     /* no more fields*/
358                 break;
359
360         case ':':
361         case '/':
362                 *s = '\0';      /* terminate */
363                 *cp = s + 1;
364                 break;
365
366         default:
367                 return NULL;
368         }
369
370         return old;
371 }
372
373 char *
374 cleanhostname(char *host)
375 {
376         if (*host == '[' && host[strlen(host) - 1] == ']') {
377                 host[strlen(host) - 1] = '\0';
378                 return (host + 1);
379         } else
380                 return host;
381 }
382
383 char *
384 colon(char *cp)
385 {
386         int flag = 0;
387
388         if (*cp == ':')         /* Leading colon is part of file name. */
389                 return (0);
390         if (*cp == '[')
391                 flag = 1;
392
393         for (; *cp; ++cp) {
394                 if (*cp == '@' && *(cp+1) == '[')
395                         flag = 1;
396                 if (*cp == ']' && *(cp+1) == ':' && flag)
397                         return (cp+1);
398                 if (*cp == ':' && !flag)
399                         return (cp);
400                 if (*cp == '/')
401                         return (0);
402         }
403         return (0);
404 }
405
406 /* function to assist building execv() arguments */
407 void
408 addargs(arglist *args, char *fmt, ...)
409 {
410         va_list ap;
411         char *cp;
412         u_int nalloc;
413         int r;
414
415         va_start(ap, fmt);
416         r = vasprintf(&cp, fmt, ap);
417         va_end(ap);
418         if (r == -1)
419                 fatal("addargs: argument too long");
420
421         nalloc = args->nalloc;
422         if (args->list == NULL) {
423                 nalloc = 32;
424                 args->num = 0;
425         } else if (args->num+2 >= nalloc)
426                 nalloc *= 2;
427
428         args->list = xrealloc(args->list, nalloc * sizeof(char *));
429         args->nalloc = nalloc;
430         args->list[args->num++] = cp;
431         args->list[args->num] = NULL;
432 }
433
434 void
435 replacearg(arglist *args, u_int which, char *fmt, ...)
436 {
437         va_list ap;
438         char *cp;
439         int r;
440
441         va_start(ap, fmt);
442         r = vasprintf(&cp, fmt, ap);
443         va_end(ap);
444         if (r == -1)
445                 fatal("replacearg: argument too long");
446
447         if (which >= args->num)
448                 fatal("replacearg: tried to replace invalid arg %d >= %d",
449                     which, args->num);
450         xfree(args->list[which]);
451         args->list[which] = cp;
452 }
453
454 void
455 freeargs(arglist *args)
456 {
457         u_int i;
458
459         if (args->list != NULL) {
460                 for (i = 0; i < args->num; i++)
461                         xfree(args->list[i]);
462                 xfree(args->list);
463                 args->nalloc = args->num = 0;
464                 args->list = NULL;
465         }
466 }
467
468 /*
469  * Expands tildes in the file name.  Returns data allocated by xmalloc.
470  * Warning: this calls getpw*.
471  */
472 char *
473 tilde_expand_filename(const char *filename, uid_t uid)
474 {
475         const char *path;
476         char user[128], ret[MAXPATHLEN];
477         struct passwd *pw;
478         u_int len, slash;
479
480         if (*filename != '~')
481                 return (xstrdup(filename));
482         filename++;
483
484         path = strchr(filename, '/');
485         if (path != NULL && path > filename) {          /* ~user/path */
486                 slash = path - filename;
487                 if (slash > sizeof(user) - 1)
488                         fatal("tilde_expand_filename: ~username too long");
489                 memcpy(user, filename, slash);
490                 user[slash] = '\0';
491                 if ((pw = getpwnam(user)) == NULL)
492                         fatal("tilde_expand_filename: No such user %s", user);
493         } else if ((pw = getpwuid(uid)) == NULL)        /* ~/path */
494                 fatal("tilde_expand_filename: No such uid %d", uid);
495
496         if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret))
497                 fatal("tilde_expand_filename: Path too long");
498
499         /* Make sure directory has a trailing '/' */
500         len = strlen(pw->pw_dir);
501         if ((len == 0 || pw->pw_dir[len - 1] != '/') &&
502             strlcat(ret, "/", sizeof(ret)) >= sizeof(ret))
503                 fatal("tilde_expand_filename: Path too long");
504
505         /* Skip leading '/' from specified path */
506         if (path != NULL)
507                 filename = path + 1;
508         if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret))
509                 fatal("tilde_expand_filename: Path too long");
510
511         return (xstrdup(ret));
512 }
513
514 /*
515  * Expand a string with a set of %[char] escapes. A number of escapes may be
516  * specified as (char *escape_chars, char *replacement) pairs. The list must
517  * be terminated by a NULL escape_char. Returns replaced string in memory
518  * allocated by xmalloc.
519  */
520 char *
521 percent_expand(const char *string, ...)
522 {
523 #define EXPAND_MAX_KEYS 16
524         struct {
525                 const char *key;
526                 const char *repl;
527         } keys[EXPAND_MAX_KEYS];
528         u_int num_keys, i, j;
529         char buf[4096];
530         va_list ap;
531
532         /* Gather keys */
533         va_start(ap, string);
534         for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) {
535                 keys[num_keys].key = va_arg(ap, char *);
536                 if (keys[num_keys].key == NULL)
537                         break;
538                 keys[num_keys].repl = va_arg(ap, char *);
539                 if (keys[num_keys].repl == NULL)
540                         fatal("percent_expand: NULL replacement");
541         }
542         va_end(ap);
543
544         if (num_keys >= EXPAND_MAX_KEYS)
545                 fatal("percent_expand: too many keys");
546
547         /* Expand string */
548         *buf = '\0';
549         for (i = 0; *string != '\0'; string++) {
550                 if (*string != '%') {
551  append:
552                         buf[i++] = *string;
553                         if (i >= sizeof(buf))
554                                 fatal("percent_expand: string too long");
555                         buf[i] = '\0';
556                         continue;
557                 }
558                 string++;
559                 if (*string == '%')
560                         goto append;
561                 for (j = 0; j < num_keys; j++) {
562                         if (strchr(keys[j].key, *string) != NULL) {
563                                 i = strlcat(buf, keys[j].repl, sizeof(buf));
564                                 if (i >= sizeof(buf))
565                                         fatal("percent_expand: string too long");
566                                 break;
567                         }
568                 }
569                 if (j >= num_keys)
570                         fatal("percent_expand: unknown key %%%c", *string);
571         }
572         return (xstrdup(buf));
573 #undef EXPAND_MAX_KEYS
574 }
575
576 /*
577  * Read an entire line from a public key file into a static buffer, discarding
578  * lines that exceed the buffer size.  Returns 0 on success, -1 on failure.
579  */
580 int
581 read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
582    u_long *lineno)
583 {
584         while (fgets(buf, bufsz, f) != NULL) {
585                 (*lineno)++;
586                 if (buf[strlen(buf) - 1] == '\n' || feof(f)) {
587                         return 0;
588                 } else {
589                         debug("%s: %s line %lu exceeds size limit", __func__,
590                             filename, *lineno);
591                         /* discard remainder of line */
592                         while (fgetc(f) != '\n' && !feof(f))
593                                 ;       /* nothing */
594                 }
595         }
596         return -1;
597 }
598
599 int
600 tun_open(int tun, int mode)
601 {
602 #if defined(CUSTOM_SYS_TUN_OPEN)
603         return (sys_tun_open(tun, mode));
604 #elif defined(SSH_TUN_OPENBSD)
605         struct ifreq ifr;
606         char name[100];
607         int fd = -1, sock;
608
609         /* Open the tunnel device */
610         if (tun <= SSH_TUNID_MAX) {
611                 snprintf(name, sizeof(name), "/dev/tun%d", tun);
612                 fd = open(name, O_RDWR);
613         } else if (tun == SSH_TUNID_ANY) {
614                 for (tun = 100; tun >= 0; tun--) {
615                         snprintf(name, sizeof(name), "/dev/tun%d", tun);
616                         if ((fd = open(name, O_RDWR)) >= 0)
617                                 break;
618                 }
619         } else {
620                 debug("%s: invalid tunnel %u", __func__, tun);
621                 return (-1);
622         }
623
624         if (fd < 0) {
625                 debug("%s: %s open failed: %s", __func__, name, strerror(errno));
626                 return (-1);
627         }
628
629         debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
630
631         /* Set the tunnel device operation mode */
632         snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun);
633         if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
634                 goto failed;
635
636         if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
637                 goto failed;
638
639         /* Set interface mode */
640         ifr.ifr_flags &= ~IFF_UP;
641         if (mode == SSH_TUNMODE_ETHERNET)
642                 ifr.ifr_flags |= IFF_LINK0;
643         else
644                 ifr.ifr_flags &= ~IFF_LINK0;
645         if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
646                 goto failed;
647
648         /* Bring interface up */
649         ifr.ifr_flags |= IFF_UP;
650         if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
651                 goto failed;
652
653         close(sock);
654         return (fd);
655
656  failed:
657         if (fd >= 0)
658                 close(fd);
659         if (sock >= 0)
660                 close(sock);
661         debug("%s: failed to set %s mode %d: %s", __func__, name,
662             mode, strerror(errno));
663         return (-1);
664 #else
665         error("Tunnel interfaces are not supported on this platform");
666         return (-1);
667 #endif
668 }
669
670 void
671 sanitise_stdfd(void)
672 {
673         int nullfd, dupfd;
674
675         if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
676                 fprintf(stderr, "Couldn't open /dev/null: %s", strerror(errno));
677                 exit(1);
678         }
679         while (++dupfd <= 2) {
680                 /* Only clobber closed fds */
681                 if (fcntl(dupfd, F_GETFL, 0) >= 0)
682                         continue;
683                 if (dup2(nullfd, dupfd) == -1) {
684                         fprintf(stderr, "dup2: %s", strerror(errno));
685                         exit(1);
686                 }
687         }
688         if (nullfd > 2)
689                 close(nullfd);
690 }
691
692 char *
693 tohex(const u_char *d, u_int l)
694 {
695         char b[3], *r;
696         u_int i, hl;
697
698         hl = l * 2 + 1;
699         r = xcalloc(1, hl);
700         for (i = 0; i < l; i++) {
701                 snprintf(b, sizeof(b), "%02x", d[i]);
702                 strlcat(r, b, hl);
703         }
704         return (r);
705 }
706
This page took 0.120773 seconds and 5 git commands to generate.