1 /* $OpenBSD: to.c,v 1.44 2019/11/12 20:21:46 gilles Exp $ */
2
3 /*
4 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
5 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
6 * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21 #include "includes.h"
22
23 #include <sys/types.h>
24 #include <sys/queue.h>
25 #include <sys/tree.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <sys/resource.h>
29
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32
33 #include <ctype.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <event.h>
37 #include <fcntl.h>
38 #include <imsg.h>
39 #include <limits.h>
40 #include <inttypes.h>
41 #include <netdb.h>
42 #include <pwd.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <time.h>
48 #include <unistd.h>
49
50 #include "smtpd.h"
51 #include "log.h"
52
53 static const char *in6addr_to_text(const struct in6_addr *);
54 static int alias_is_filter(struct expandnode *, const char *, size_t);
55 static int alias_is_username(struct expandnode *, const char *, size_t);
56 static int alias_is_address(struct expandnode *, const char *, size_t);
57 static int alias_is_filename(struct expandnode *, const char *, size_t);
58 static int alias_is_include(struct expandnode *, const char *, size_t);
59 static int alias_is_error(struct expandnode *, const char *, size_t);
60
61 static int broken_inet_net_pton_ipv6(const char *, void *, size_t);
62
63 const char *
sockaddr_to_text(struct sockaddr * sa)64 sockaddr_to_text(struct sockaddr *sa)
65 {
66 static char buf[NI_MAXHOST];
67
68 if (getnameinfo(sa, SA_LEN(sa), buf, sizeof(buf), NULL, 0,
69 NI_NUMERICHOST))
70 return ("(unknown)");
71 else
72 return (buf);
73 }
74
75 static const char *
in6addr_to_text(const struct in6_addr * addr)76 in6addr_to_text(const struct in6_addr *addr)
77 {
78 struct sockaddr_in6 sa_in6;
79 uint16_t tmp16;
80
81 memset(&sa_in6, 0, sizeof(sa_in6));
82 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
83 sa_in6.sin6_len = sizeof(sa_in6);
84 #endif
85 sa_in6.sin6_family = AF_INET6;
86 memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
87
88 /* XXX thanks, KAME, for this ugliness... adopted from route/show.c */
89 if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) ||
90 IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) {
91 memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16));
92 sa_in6.sin6_scope_id = ntohs(tmp16);
93 sa_in6.sin6_addr.s6_addr[2] = 0;
94 sa_in6.sin6_addr.s6_addr[3] = 0;
95 }
96
97 return (sockaddr_to_text((struct sockaddr *)&sa_in6));
98 }
99
100 int
text_to_mailaddr(struct mailaddr * maddr,const char * email)101 text_to_mailaddr(struct mailaddr *maddr, const char *email)
102 {
103 char *username;
104 char *hostname;
105 char buffer[LINE_MAX];
106
107 if (strlcpy(buffer, email, sizeof buffer) >= sizeof buffer)
108 return 0;
109
110 memset(maddr, 0, sizeof *maddr);
111
112 username = buffer;
113 hostname = strrchr(username, '@');
114
115 if (hostname == NULL) {
116 if (strlcpy(maddr->user, username, sizeof maddr->user)
117 >= sizeof maddr->user)
118 return 0;
119 }
120 else if (username == hostname) {
121 *hostname++ = '\0';
122 if (strlcpy(maddr->domain, hostname, sizeof maddr->domain)
123 >= sizeof maddr->domain)
124 return 0;
125 }
126 else {
127 *hostname++ = '\0';
128 if (strlcpy(maddr->user, username, sizeof maddr->user)
129 >= sizeof maddr->user)
130 return 0;
131 if (strlcpy(maddr->domain, hostname, sizeof maddr->domain)
132 >= sizeof maddr->domain)
133 return 0;
134 }
135
136 return 1;
137 }
138
139 const char *
mailaddr_to_text(const struct mailaddr * maddr)140 mailaddr_to_text(const struct mailaddr *maddr)
141 {
142 static char buffer[LINE_MAX];
143
144 (void)strlcpy(buffer, maddr->user, sizeof buffer);
145 (void)strlcat(buffer, "@", sizeof buffer);
146 if (strlcat(buffer, maddr->domain, sizeof buffer) >= sizeof buffer)
147 return NULL;
148
149 return buffer;
150 }
151
152
153 const char *
sa_to_text(const struct sockaddr * sa)154 sa_to_text(const struct sockaddr *sa)
155 {
156 static char buf[NI_MAXHOST + 5];
157 char *p;
158
159 buf[0] = '\0';
160 p = buf;
161
162 if (sa->sa_family == AF_LOCAL)
163 (void)strlcpy(buf, "local", sizeof buf);
164 else if (sa->sa_family == AF_INET) {
165 in_addr_t addr;
166
167 addr = ((const struct sockaddr_in *)sa)->sin_addr.s_addr;
168 addr = ntohl(addr);
169 (void)bsnprintf(p, NI_MAXHOST, "%d.%d.%d.%d",
170 (addr >> 24) & 0xff, (addr >> 16) & 0xff,
171 (addr >> 8) & 0xff, addr & 0xff);
172 }
173 else if (sa->sa_family == AF_INET6) {
174 const struct sockaddr_in6 *in6;
175 const struct in6_addr *in6_addr;
176
177 in6 = (const struct sockaddr_in6 *)sa;
178 p = buf;
179 in6_addr = &in6->sin6_addr;
180 (void)bsnprintf(p, NI_MAXHOST, "[%s]", in6addr_to_text(in6_addr));
181 }
182
183 return (buf);
184 }
185
186 const char *
ss_to_text(const struct sockaddr_storage * ss)187 ss_to_text(const struct sockaddr_storage *ss)
188 {
189 return (sa_to_text((const struct sockaddr*)ss));
190 }
191
192 const char *
time_to_text(time_t when)193 time_to_text(time_t when)
194 {
195 struct tm *lt;
196 static char buf[40];
197 char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
198 char *month[] = {"Jan","Feb","Mar","Apr","May","Jun",
199 "Jul","Aug","Sep","Oct","Nov","Dec"};
200 const char *tz;
201 long offset;
202
203 lt = localtime(&when);
204 if (lt == NULL || when == 0)
205 fatalx("time_to_text: localtime");
206
207 #if HAVE_STRUCT_TM_TM_GMTOFF
208 offset = lt->tm_gmtoff;
209 tz = lt->tm_zone;
210 #elif defined HAVE_DECL_ALTZONE && defined HAVE_DECL_TIMEZONE
211 offset = lt->tm_isdst > 0 ? altzone : timezone;
212 tz = lt->tm_isdst > 0 ? tzname[1] : tzname[0];
213 #endif
214
215 /* We do not use strftime because it is subject to locale substitution*/
216 if (!bsnprintf(buf, sizeof(buf),
217 "%s, %d %s %d %02d:%02d:%02d %c%02d%02d (%s)",
218 day[lt->tm_wday], lt->tm_mday, month[lt->tm_mon],
219 lt->tm_year + 1900,
220 lt->tm_hour, lt->tm_min, lt->tm_sec,
221 offset >= 0 ? '+' : '-',
222 abs((int)offset / 3600),
223 abs((int)offset % 3600) / 60,
224 tz))
225 fatalx("time_to_text: bsnprintf");
226
227 return buf;
228 }
229
230 const char *
duration_to_text(time_t t)231 duration_to_text(time_t t)
232 {
233 static char dst[64];
234 char buf[64];
235 int h, m, s;
236 long long d;
237
238 if (t == 0) {
239 (void)strlcpy(dst, "0s", sizeof dst);
240 return (dst);
241 }
242
243 dst[0] = '\0';
244 if (t < 0) {
245 (void)strlcpy(dst, "-", sizeof dst);
246 t = -t;
247 }
248
249 s = t % 60;
250 t /= 60;
251 m = t % 60;
252 t /= 60;
253 h = t % 24;
254 d = t / 24;
255
256 if (d) {
257 (void)snprintf(buf, sizeof buf, "%lldd", d);
258 (void)strlcat(dst, buf, sizeof dst);
259 }
260 if (h) {
261 (void)snprintf(buf, sizeof buf, "%dh", h);
262 (void)strlcat(dst, buf, sizeof dst);
263 }
264 if (m) {
265 (void)snprintf(buf, sizeof buf, "%dm", m);
266 (void)strlcat(dst, buf, sizeof dst);
267 }
268 if (s) {
269 (void)snprintf(buf, sizeof buf, "%ds", s);
270 (void)strlcat(dst, buf, sizeof dst);
271 }
272
273 return (dst);
274 }
275
276 int
text_to_netaddr(struct netaddr * netaddr,const char * s)277 text_to_netaddr(struct netaddr *netaddr, const char *s)
278 {
279 struct sockaddr_storage ss;
280 struct sockaddr_in ssin;
281 struct sockaddr_in6 ssin6;
282 int bits;
283 char buf[NI_MAXHOST];
284 size_t len;
285
286 memset(&ssin, 0, sizeof(struct sockaddr_in));
287 memset(&ssin6, 0, sizeof(struct sockaddr_in6));
288
289 if (strncasecmp("IPv6:", s, 5) == 0)
290 s += 5;
291
292 bits = inet_net_pton(AF_INET, s, &ssin.sin_addr,
293 sizeof(struct in_addr));
294 if (bits != -1) {
295 ssin.sin_family = AF_INET;
296 memcpy(&ss, &ssin, sizeof(ssin));
297 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
298 ss.ss_len = sizeof(struct sockaddr_in);
299 #endif
300 } else {
301 if (s[0] != '[') {
302 if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf)
303 return 0;
304 }
305 else {
306 s++;
307 if (strncasecmp("IPv6:", s, 5) == 0)
308 s += 5;
309 if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf)
310 return 0;
311 if (buf[len-1] != ']')
312 return 0;
313 buf[len-1] = 0;
314 }
315 bits = inet_net_pton(AF_INET6, buf, &ssin6.sin6_addr,
316 sizeof(struct in6_addr));
317 if (bits == -1) {
318 if (errno != EAFNOSUPPORT)
319 return 0;
320 bits = broken_inet_net_pton_ipv6(buf, &ssin6.sin6_addr,
321 sizeof(struct in6_addr));
322 if (bits == -1)
323 return 0;
324 }
325 ssin6.sin6_family = AF_INET6;
326 memcpy(&ss, &ssin6, sizeof(ssin6));
327 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
328 ss.ss_len = sizeof(struct sockaddr_in6);
329 #endif
330 }
331
332 netaddr->ss = ss;
333 netaddr->bits = bits;
334 return 1;
335 }
336
337 int
text_to_relayhost(struct relayhost * relay,const char * s)338 text_to_relayhost(struct relayhost *relay, const char *s)
339 {
340 static const struct schema {
341 const char *name;
342 int tls;
343 uint16_t flags;
344 uint16_t port;
345 } schemas [] = {
346 /*
347 * new schemas should be *appended* otherwise the default
348 * schema index needs to be updated later in this function.
349 */
350 { "smtp://", RELAY_TLS_OPPORTUNISTIC, 0, 25 },
351 { "smtp+tls://", RELAY_TLS_STARTTLS, 0, 25 },
352 { "smtp+notls://", RELAY_TLS_NO, 0, 25 },
353 /* need to specify an explicit port for LMTP */
354 { "lmtp://", RELAY_TLS_NO, RELAY_LMTP, 0 },
355 { "smtps://", RELAY_TLS_SMTPS, 0, 465 }
356 };
357 const char *errstr = NULL;
358 char *p, *q;
359 char buffer[1024];
360 char *beg, *end;
361 size_t i;
362 size_t len;
363
364 memset(buffer, 0, sizeof buffer);
365 if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
366 return 0;
367
368 for (i = 0; i < nitems(schemas); ++i)
369 if (strncasecmp(schemas[i].name, s,
370 strlen(schemas[i].name)) == 0)
371 break;
372
373 if (i == nitems(schemas)) {
374 /* there is a schema, but it's not recognized */
375 if (strstr(buffer, "://"))
376 return 0;
377
378 /* no schema, default to smtp:// */
379 i = 0;
380 p = buffer;
381 }
382 else
383 p = buffer + strlen(schemas[i].name);
384
385 relay->tls = schemas[i].tls;
386 relay->flags = schemas[i].flags;
387 relay->port = schemas[i].port;
388
389 /* first, we extract the label if any */
390 if ((q = strchr(p, '@')) != NULL) {
391 *q = 0;
392 if (strlcpy(relay->authlabel, p, sizeof (relay->authlabel))
393 >= sizeof (relay->authlabel))
394 return 0;
395 p = q + 1;
396 }
397
398 /* then, we extract the mail exchanger */
399 beg = end = p;
400 if (*beg == '[') {
401 if ((end = strchr(beg, ']')) == NULL)
402 return 0;
403 /* skip ']', it has to be included in the relay hostname */
404 ++end;
405 len = end - beg;
406 }
407 else {
408 for (end = beg; *end; ++end)
409 if (!isalnum((unsigned char)*end) &&
410 *end != '_' && *end != '.' && *end != '-')
411 break;
412 len = end - beg;
413 }
414 if (len >= sizeof relay->hostname)
415 return 0;
416 for (i = 0; i < len; ++i)
417 relay->hostname[i] = beg[i];
418 relay->hostname[i] = 0;
419
420 /* finally, we extract the port */
421 p = beg + len;
422 if (*p == ':') {
423 relay->port = strtonum(p+1, 1, IPPORT_HILASTAUTO, &errstr);
424 if (errstr)
425 return 0;
426 }
427
428 if (!valid_domainpart(relay->hostname))
429 return 0;
430 if ((relay->flags & RELAY_LMTP) && (relay->port == 0))
431 return 0;
432 if (relay->authlabel[0]) {
433 /* disallow auth on non-tls scheme. */
434 if (relay->tls != RELAY_TLS_STARTTLS &&
435 relay->tls != RELAY_TLS_SMTPS)
436 return 0;
437 relay->flags |= RELAY_AUTH;
438 }
439
440 return 1;
441 }
442
443 uint64_t
text_to_evpid(const char * s)444 text_to_evpid(const char *s)
445 {
446 uint64_t ulval;
447 char *ep;
448
449 errno = 0;
450 ulval = strtoull(s, &ep, 16);
451 if (s[0] == '\0' || *ep != '\0')
452 return 0;
453 if (errno == ERANGE && ulval == ULLONG_MAX)
454 return 0;
455 if (ulval == 0)
456 return 0;
457 return (ulval);
458 }
459
460 uint32_t
text_to_msgid(const char * s)461 text_to_msgid(const char *s)
462 {
463 uint64_t ulval;
464 char *ep;
465
466 errno = 0;
467 ulval = strtoull(s, &ep, 16);
468 if (s[0] == '\0' || *ep != '\0')
469 return 0;
470 if (errno == ERANGE && ulval == ULLONG_MAX)
471 return 0;
472 if (ulval == 0)
473 return 0;
474 if (ulval > 0xffffffff)
475 return 0;
476 return (ulval & 0xffffffff);
477 }
478
479 const char *
rule_to_text(struct rule * r)480 rule_to_text(struct rule *r)
481 {
482 static char buf[4096];
483
484 memset(buf, 0, sizeof buf);
485 (void)strlcpy(buf, "match", sizeof buf);
486 if (r->flag_tag) {
487 if (r->flag_tag < 0)
488 (void)strlcat(buf, " !", sizeof buf);
489 (void)strlcat(buf, " tag ", sizeof buf);
490 (void)strlcat(buf, r->table_tag, sizeof buf);
491 }
492
493 if (r->flag_from) {
494 if (r->flag_from < 0)
495 (void)strlcat(buf, " !", sizeof buf);
496 if (r->flag_from_socket)
497 (void)strlcat(buf, " from socket", sizeof buf);
498 else if (r->flag_from_rdns) {
499 (void)strlcat(buf, " from rdns", sizeof buf);
500 if (r->table_from) {
501 (void)strlcat(buf, " ", sizeof buf);
502 (void)strlcat(buf, r->table_from, sizeof buf);
503 }
504 }
505 else if (strcmp(r->table_from, "<anyhost>") == 0)
506 (void)strlcat(buf, " from any", sizeof buf);
507 else if (strcmp(r->table_from, "<localhost>") == 0)
508 (void)strlcat(buf, " from local", sizeof buf);
509 else {
510 (void)strlcat(buf, " from src ", sizeof buf);
511 (void)strlcat(buf, r->table_from, sizeof buf);
512 }
513 }
514
515 if (r->flag_for) {
516 if (r->flag_for < 0)
517 (void)strlcat(buf, " !", sizeof buf);
518 if (strcmp(r->table_for, "<anydestination>") == 0)
519 (void)strlcat(buf, " for any", sizeof buf);
520 else if (strcmp(r->table_for, "<localnames>") == 0)
521 (void)strlcat(buf, " for local", sizeof buf);
522 else {
523 (void)strlcat(buf, " for domain ", sizeof buf);
524 (void)strlcat(buf, r->table_for, sizeof buf);
525 }
526 }
527
528 if (r->flag_smtp_helo) {
529 if (r->flag_smtp_helo < 0)
530 (void)strlcat(buf, " !", sizeof buf);
531 (void)strlcat(buf, " helo ", sizeof buf);
532 (void)strlcat(buf, r->table_smtp_helo, sizeof buf);
533 }
534
535 if (r->flag_smtp_auth) {
536 if (r->flag_smtp_auth < 0)
537 (void)strlcat(buf, " !", sizeof buf);
538 (void)strlcat(buf, " auth", sizeof buf);
539 if (r->table_smtp_auth) {
540 (void)strlcat(buf, " ", sizeof buf);
541 (void)strlcat(buf, r->table_smtp_auth, sizeof buf);
542 }
543 }
544
545 if (r->flag_smtp_starttls) {
546 if (r->flag_smtp_starttls < 0)
547 (void)strlcat(buf, " !", sizeof buf);
548 (void)strlcat(buf, " tls", sizeof buf);
549 }
550
551 if (r->flag_smtp_mail_from) {
552 if (r->flag_smtp_mail_from < 0)
553 (void)strlcat(buf, " !", sizeof buf);
554 (void)strlcat(buf, " mail-from ", sizeof buf);
555 (void)strlcat(buf, r->table_smtp_mail_from, sizeof buf);
556 }
557
558 if (r->flag_smtp_rcpt_to) {
559 if (r->flag_smtp_rcpt_to < 0)
560 (void)strlcat(buf, " !", sizeof buf);
561 (void)strlcat(buf, " rcpt-to ", sizeof buf);
562 (void)strlcat(buf, r->table_smtp_rcpt_to, sizeof buf);
563 }
564 (void)strlcat(buf, " action ", sizeof buf);
565 if (r->reject)
566 (void)strlcat(buf, "reject", sizeof buf);
567 else
568 (void)strlcat(buf, r->dispatcher, sizeof buf);
569 return buf;
570 }
571
572
573 int
text_to_userinfo(struct userinfo * userinfo,const char * s)574 text_to_userinfo(struct userinfo *userinfo, const char *s)
575 {
576 char buf[PATH_MAX];
577 char *p;
578 const char *errstr;
579
580 memset(buf, 0, sizeof buf);
581 p = buf;
582 while (*s && *s != ':')
583 *p++ = *s++;
584 if (*s++ != ':')
585 goto error;
586
587 if (strlcpy(userinfo->username, buf,
588 sizeof userinfo->username) >= sizeof userinfo->username)
589 goto error;
590
591 memset(buf, 0, sizeof buf);
592 p = buf;
593 while (*s && *s != ':')
594 *p++ = *s++;
595 if (*s++ != ':')
596 goto error;
597 userinfo->uid = strtonum(buf, 0, UID_MAX, &errstr);
598 if (errstr)
599 goto error;
600
601 memset(buf, 0, sizeof buf);
602 p = buf;
603 while (*s && *s != ':')
604 *p++ = *s++;
605 if (*s++ != ':')
606 goto error;
607 userinfo->gid = strtonum(buf, 0, GID_MAX, &errstr);
608 if (errstr)
609 goto error;
610
611 if (strlcpy(userinfo->directory, s,
612 sizeof userinfo->directory) >= sizeof userinfo->directory)
613 goto error;
614
615 return 1;
616
617 error:
618 return 0;
619 }
620
621 int
text_to_credentials(struct credentials * creds,const char * s)622 text_to_credentials(struct credentials *creds, const char *s)
623 {
624 char *p;
625 char buffer[LINE_MAX];
626 size_t offset;
627
628 p = strchr(s, ':');
629 if (p == NULL) {
630 creds->username[0] = '\0';
631 if (strlcpy(creds->password, s, sizeof creds->password)
632 >= sizeof creds->password)
633 return 0;
634 return 1;
635 }
636
637 offset = p - s;
638
639 memset(buffer, 0, sizeof buffer);
640 if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
641 return 0;
642 p = buffer + offset;
643 *p = '\0';
644
645 if (strlcpy(creds->username, buffer, sizeof creds->username)
646 >= sizeof creds->username)
647 return 0;
648 if (strlcpy(creds->password, p+1, sizeof creds->password)
649 >= sizeof creds->password)
650 return 0;
651
652 return 1;
653 }
654
655 int
text_to_expandnode(struct expandnode * expandnode,const char * s)656 text_to_expandnode(struct expandnode *expandnode, const char *s)
657 {
658 size_t l;
659
660 l = strlen(s);
661 if (alias_is_error(expandnode, s, l) ||
662 alias_is_include(expandnode, s, l) ||
663 alias_is_filter(expandnode, s, l) ||
664 alias_is_filename(expandnode, s, l) ||
665 alias_is_address(expandnode, s, l) ||
666 alias_is_username(expandnode, s, l))
667 return (1);
668
669 return (0);
670 }
671
672 const char *
expandnode_to_text(struct expandnode * expandnode)673 expandnode_to_text(struct expandnode *expandnode)
674 {
675 switch (expandnode->type) {
676 case EXPAND_FILTER:
677 case EXPAND_FILENAME:
678 case EXPAND_INCLUDE:
679 case EXPAND_ERROR:
680 case EXPAND_USERNAME:
681 return expandnode->u.user;
682 case EXPAND_ADDRESS:
683 return mailaddr_to_text(&expandnode->u.mailaddr);
684 case EXPAND_INVALID:
685 break;
686 }
687
688 return NULL;
689 }
690
691 /******/
692 static int
alias_is_filter(struct expandnode * alias,const char * line,size_t len)693 alias_is_filter(struct expandnode *alias, const char *line, size_t len)
694 {
695 int v = 0;
696
697 if (*line == '"')
698 v = 1;
699 if (*(line+v) == '|') {
700 if (strlcpy(alias->u.buffer, line + v + 1,
701 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer))
702 return 0;
703 if (v) {
704 v = strlen(alias->u.buffer);
705 if (v == 0)
706 return (0);
707 if (alias->u.buffer[v-1] != '"')
708 return (0);
709 alias->u.buffer[v-1] = '\0';
710 }
711 alias->type = EXPAND_FILTER;
712 return (1);
713 }
714 return (0);
715 }
716
717 static int
alias_is_username(struct expandnode * alias,const char * line,size_t len)718 alias_is_username(struct expandnode *alias, const char *line, size_t len)
719 {
720 memset(alias, 0, sizeof *alias);
721
722 if (strlcpy(alias->u.user, line,
723 sizeof(alias->u.user)) >= sizeof(alias->u.user))
724 return 0;
725
726 while (*line) {
727 if (!isalnum((unsigned char)*line) &&
728 *line != '_' && *line != '.' && *line != '-' && *line != '+')
729 return 0;
730 ++line;
731 }
732
733 alias->type = EXPAND_USERNAME;
734 return 1;
735 }
736
737 static int
alias_is_address(struct expandnode * alias,const char * line,size_t len)738 alias_is_address(struct expandnode *alias, const char *line, size_t len)
739 {
740 char *domain;
741
742 memset(alias, 0, sizeof *alias);
743
744 if (len < 3) /* x@y */
745 return 0;
746
747 domain = strchr(line, '@');
748 if (domain == NULL)
749 return 0;
750
751 /* @ cannot start or end an address */
752 if (domain == line || domain == line + len - 1)
753 return 0;
754
755 /* scan pre @ for disallowed chars */
756 *domain++ = '\0';
757 (void)strlcpy(alias->u.mailaddr.user, line, sizeof(alias->u.mailaddr.user));
758 (void)strlcpy(alias->u.mailaddr.domain, domain,
759 sizeof(alias->u.mailaddr.domain));
760
761 while (*line) {
762 char allowedset[] = "!#$%*/?|^{}`~&'+-=_.";
763 if (!isalnum((unsigned char)*line) &&
764 strchr(allowedset, *line) == NULL)
765 return 0;
766 ++line;
767 }
768
769 while (*domain) {
770 char allowedset[] = "-.";
771 if (!isalnum((unsigned char)*domain) &&
772 strchr(allowedset, *domain) == NULL)
773 return 0;
774 ++domain;
775 }
776
777 alias->type = EXPAND_ADDRESS;
778 return 1;
779 }
780
781 static int
alias_is_filename(struct expandnode * alias,const char * line,size_t len)782 alias_is_filename(struct expandnode *alias, const char *line, size_t len)
783 {
784 memset(alias, 0, sizeof *alias);
785
786 if (*line != '/')
787 return 0;
788
789 if (strlcpy(alias->u.buffer, line,
790 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer))
791 return 0;
792 alias->type = EXPAND_FILENAME;
793 return 1;
794 }
795
796 static int
alias_is_include(struct expandnode * alias,const char * line,size_t len)797 alias_is_include(struct expandnode *alias, const char *line, size_t len)
798 {
799 size_t skip;
800
801 memset(alias, 0, sizeof *alias);
802
803 if (strncasecmp(":include:", line, 9) == 0)
804 skip = 9;
805 else if (strncasecmp("include:", line, 8) == 0)
806 skip = 8;
807 else
808 return 0;
809
810 if (!alias_is_filename(alias, line + skip, len - skip))
811 return 0;
812
813 alias->type = EXPAND_INCLUDE;
814 return 1;
815 }
816
817 static int
alias_is_error(struct expandnode * alias,const char * line,size_t len)818 alias_is_error(struct expandnode *alias, const char *line, size_t len)
819 {
820 size_t skip;
821
822 memset(alias, 0, sizeof *alias);
823
824 if (strncasecmp(":error:", line, 7) == 0)
825 skip = 7;
826 else if (strncasecmp("error:", line, 6) == 0)
827 skip = 6;
828 else
829 return 0;
830
831 if (strlcpy(alias->u.buffer, line + skip,
832 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer))
833 return 0;
834
835 if (strlen(alias->u.buffer) < 5)
836 return 0;
837
838 /* [45][0-9]{2} [a-zA-Z0-9].* */
839 if (alias->u.buffer[3] != ' ' ||
840 !isalnum((unsigned char)alias->u.buffer[4]) ||
841 (alias->u.buffer[0] != '4' && alias->u.buffer[0] != '5') ||
842 !isdigit((unsigned char)alias->u.buffer[1]) ||
843 !isdigit((unsigned char)alias->u.buffer[2]))
844 return 0;
845
846 alias->type = EXPAND_ERROR;
847 return 1;
848 }
849
850 static int
broken_inet_net_pton_ipv6(const char * src,void * dst,size_t size)851 broken_inet_net_pton_ipv6(const char *src, void *dst, size_t size)
852 {
853 int ret;
854 int bits;
855 char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")];
856 char *sep;
857 const char *errstr;
858
859 if (strlcpy(buf, src, sizeof buf) >= sizeof buf) {
860 errno = EMSGSIZE;
861 return (-1);
862 }
863
864 sep = strchr(buf, '/');
865 if (sep != NULL)
866 *sep++ = '\0';
867
868 ret = inet_pton(AF_INET6, buf, dst);
869 if (ret != 1)
870 return (-1);
871
872 if (sep == NULL)
873 return 128;
874
875 bits = strtonum(sep, 0, 128, &errstr);
876 if (errstr)
877 return (-1);
878
879 return bits;
880 }
881