Line data Source code
1 : /* mbox-util.c - Mail address helper functions
2 : * Copyright (C) 1998-2010 Free Software Foundation, Inc.
3 : * Copyright (C) 1998-2015 Werner Koch
4 : *
5 : * This file is part of GnuPG.
6 : *
7 : * This file is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU Lesser General Public License as
9 : * published by the Free Software Foundation; either version 2.1 of
10 : * the License, or (at your option) any later version.
11 : *
12 : * This file is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU Lesser General Public License
18 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
19 : */
20 :
21 : /* NB: GPGME uses the same code to reflect our idea on how to extract
22 : * a mail address from a user id.
23 : */
24 :
25 : #include <config.h>
26 : #include <stdio.h>
27 : #include <stdlib.h>
28 : #include <string.h>
29 : #include <unistd.h>
30 : #include <errno.h>
31 :
32 : #include "util.h"
33 : #include "mbox-util.h"
34 :
35 :
36 : static int
37 424 : string_count_chr (const char *string, int c)
38 : {
39 : int count;
40 :
41 5400 : for (count=0; *string; string++ )
42 4976 : if ( *string == c )
43 426 : count++;
44 424 : return count;
45 : }
46 :
47 : static int
48 558 : mem_count_chr (const void *buffer, int c, size_t length)
49 : {
50 558 : const char *s = buffer;
51 : int count;
52 :
53 7793 : for (count=0; length; length--, s++)
54 7235 : if (*s == c)
55 314 : count++;
56 558 : return count;
57 : }
58 :
59 :
60 : /* This is a case-sensitive version of our memistr. I wonder why no
61 : standard function memstr exists but I better do not use the name
62 : memstr to avoid future conflicts. */
63 : static const char *
64 313 : my_memstr (const void *buffer, size_t buflen, const char *sub)
65 : {
66 313 : const unsigned char *buf = buffer;
67 313 : const unsigned char *t = (const unsigned char *)buf;
68 313 : const unsigned char *s = (const unsigned char *)sub;
69 313 : size_t n = buflen;
70 :
71 5191 : for ( ; n ; t++, n-- )
72 : {
73 4878 : if (*t == *s)
74 : {
75 313 : for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
76 : ;
77 313 : if (!*s)
78 0 : return (const char*)buf;
79 313 : t = (const unsigned char *)buf;
80 313 : s = (const unsigned char *)sub ;
81 313 : n = buflen;
82 : }
83 : }
84 313 : return NULL;
85 : }
86 :
87 :
88 :
89 : static int
90 419 : string_has_ctrl_or_space (const char *string)
91 : {
92 5329 : for (; *string; string++ )
93 4911 : if (!(*string & 0x80) && *string <= 0x20)
94 1 : return 1;
95 418 : return 0;
96 : }
97 :
98 :
99 : /* Return true if STRING has two consecutive '.' after an '@'
100 : sign. */
101 : static int
102 418 : has_dotdot_after_at (const char *string)
103 : {
104 418 : string = strchr (string, '@');
105 418 : if (!string)
106 0 : return 0; /* No at-sign. */
107 418 : string++;
108 418 : return !!strstr (string, "..");
109 : }
110 :
111 :
112 : /* Check whether BUFFER has characters not valid in an RFC-822
113 : address. LENGTH gives the length of BUFFER.
114 :
115 : To cope with OpenPGP we ignore non-ascii characters so that for
116 : example umlauts are legal in an email address. An OpenPGP user ID
117 : must be utf-8 encoded but there is no strict requirement for
118 : RFC-822. Thus to avoid IDNA encoding we put the address verbatim
119 : as utf-8 into the user ID under the assumption that mail programs
120 : handle IDNA at a lower level and take OpenPGP user IDs as utf-8.
121 : Note that we can't do an utf-8 encoding checking here because in
122 : keygen.c this function is called with the native encoding and
123 : native to utf-8 encoding is only done later. */
124 : int
125 764 : has_invalid_email_chars (const void *buffer, size_t length)
126 : {
127 764 : const unsigned char *s = buffer;
128 764 : int at_seen=0;
129 764 : const char *valid_chars=
130 : "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
131 :
132 9434 : for ( ; length && *s; length--, s++ )
133 : {
134 8876 : if ((*s & 0x80))
135 0 : continue; /* We only care about ASCII. */
136 8876 : if (*s == '@')
137 315 : at_seen=1;
138 8766 : else if (!at_seen && !(strchr (valid_chars, *s)
139 205 : || strchr ("!#$%&'*+/=?^`{|}~", *s)))
140 205 : return 1;
141 8356 : else if (at_seen && !strchr (valid_chars, *s))
142 1 : return 1;
143 : }
144 558 : return 0;
145 : }
146 :
147 :
148 : /* Same as is_valid_mailbox (see below) but operates on non-nul
149 : terminated buffer. */
150 : int
151 765 : is_valid_mailbox_mem (const void *name_arg, size_t namelen)
152 : {
153 765 : const char *name = name_arg;
154 :
155 1078 : return !( !name
156 765 : || !namelen
157 764 : || has_invalid_email_chars (name, namelen)
158 558 : || mem_count_chr (name, '@', namelen) != 1
159 314 : || *name == '@'
160 313 : || name[namelen-1] == '@'
161 313 : || name[namelen-1] == '.'
162 313 : || my_memstr (name, namelen, ".."));
163 : }
164 :
165 :
166 : /* Check whether NAME represents a valid mailbox according to
167 : RFC822. Returns true if so. */
168 : int
169 765 : is_valid_mailbox (const char *name)
170 : {
171 765 : return name? is_valid_mailbox_mem (name, strlen (name)) : 0;
172 : }
173 :
174 :
175 : /* Return the mailbox (local-part@domain) form a standard user id.
176 : All plain ASCII characters in the result are converted to
177 : lowercase. Caller must free the result. Returns NULL if no valid
178 : mailbox was found (or we are out of memory). */
179 : char *
180 636 : mailbox_from_userid (const char *userid)
181 : {
182 : const char *s, *s_end;
183 : size_t len;
184 636 : char *result = NULL;
185 :
186 636 : s = strchr (userid, '<');
187 636 : if (s)
188 : {
189 : /* Seems to be a standard user id. */
190 427 : s++;
191 427 : s_end = strchr (s, '>');
192 427 : if (s_end && s_end > s)
193 : {
194 424 : len = s_end - s;
195 424 : result = xtrymalloc (len + 1);
196 424 : if (!result)
197 0 : return NULL; /* Ooops - out of core. */
198 424 : strncpy (result, s, len);
199 424 : result[len] = 0;
200 : /* Apply some basic checks on the address. We do not use
201 : is_valid_mailbox because those checks are too strict. */
202 848 : if (string_count_chr (result, '@') != 1 /* Need exactly one '@. */
203 422 : || *result == '@' /* local-part missing. */
204 421 : || result[len-1] == '@' /* domain missing. */
205 421 : || result[len-1] == '.' /* ends with a dot. */
206 419 : || string_has_ctrl_or_space (result)
207 418 : || has_dotdot_after_at (result))
208 : {
209 7 : xfree (result);
210 7 : result = NULL;
211 7 : errno = EINVAL;
212 : }
213 : }
214 : else
215 3 : errno = EINVAL;
216 : }
217 209 : else if (is_valid_mailbox (userid))
218 : {
219 : /* The entire user id is a mailbox. Return that one. Note that
220 : this fallback method has some restrictions on the valid
221 : syntax of the mailbox. However, those who want weird
222 : addresses should know about it and use the regular <...>
223 : syntax. */
224 1 : result = xtrystrdup (userid);
225 : }
226 : else
227 208 : errno = EINVAL;
228 :
229 636 : return result? ascii_strlwr (result): NULL;
230 : }
231 :
232 :
233 : /* Check whether UID is a valid standard user id of the form
234 : "Heinrich Heine <heinrichh@duesseldorf.de>"
235 : and return true if this is the case. */
236 : int
237 1 : is_valid_user_id (const char *uid)
238 : {
239 1 : if (!uid || !*uid)
240 0 : return 0;
241 :
242 1 : return 1;
243 : }
|