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 <http://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 32 : string_count_chr (const char *string, int c)
38 : {
39 : int count;
40 :
41 491 : for (count=0; *string; string++ )
42 459 : if ( *string == c )
43 34 : count++;
44 32 : return count;
45 : }
46 :
47 : static int
48 251 : mem_count_chr (const void *buffer, int c, size_t length)
49 : {
50 251 : const char *s = buffer;
51 : int count;
52 :
53 3317 : for (count=0; length; length--, s++)
54 3066 : if (*s == c)
55 127 : count++;
56 251 : 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 126 : my_memstr (const void *buffer, size_t buflen, const char *sub)
65 : {
66 126 : const unsigned char *buf = buffer;
67 126 : const unsigned char *t = (const unsigned char *)buf;
68 126 : const unsigned char *s = (const unsigned char *)sub;
69 126 : size_t n = buflen;
70 :
71 2013 : for ( ; n ; t++, n-- )
72 : {
73 1887 : if (*t == *s)
74 : {
75 126 : for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
76 : ;
77 126 : if (!*s)
78 0 : return (const char*)buf;
79 126 : t = (const unsigned char *)buf;
80 126 : s = (const unsigned char *)sub ;
81 126 : n = buflen;
82 : }
83 : }
84 126 : return NULL;
85 : }
86 :
87 :
88 :
89 : static int
90 27 : string_has_ctrl_or_space (const char *string)
91 : {
92 420 : for (; *string; string++ )
93 394 : if (!(*string & 0x80) && *string <= 0x20)
94 1 : return 1;
95 26 : return 0;
96 : }
97 :
98 :
99 : /* Return true if STRING has two consecutive '.' after an '@'
100 : sign. */
101 : static int
102 26 : has_dotdot_after_at (const char *string)
103 : {
104 26 : string = strchr (string, '@');
105 26 : if (!string)
106 0 : return 0; /* No at-sign. */
107 26 : string++;
108 26 : 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 380 : has_invalid_email_chars (const void *buffer, size_t length)
126 : {
127 380 : const unsigned char *s = buffer;
128 380 : int at_seen=0;
129 380 : const char *valid_chars=
130 : "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
131 :
132 4342 : for ( ; length && *s; length--, s++ )
133 : {
134 4091 : if ((*s & 0x80))
135 0 : continue; /* We only care about ASCII. */
136 4091 : if (*s == '@')
137 128 : at_seen=1;
138 4091 : else if (!at_seen && !(strchr (valid_chars, *s)
139 128 : || strchr ("!#$%&'*+/=?^`{|}~", *s)))
140 128 : return 1;
141 3835 : else if (at_seen && !strchr (valid_chars, *s))
142 1 : return 1;
143 : }
144 251 : return 0;
145 : }
146 :
147 :
148 : /* Same as is_valid_mailbox (see below) but operates on non-nul
149 : terminated buffer. */
150 : int
151 381 : is_valid_mailbox_mem (const void *name_arg, size_t namelen)
152 : {
153 381 : const char *name = name_arg;
154 :
155 507 : return !( !name
156 381 : || !namelen
157 380 : || has_invalid_email_chars (name, namelen)
158 251 : || mem_count_chr (name, '@', namelen) != 1
159 127 : || *name == '@'
160 126 : || name[namelen-1] == '@'
161 126 : || name[namelen-1] == '.'
162 126 : || 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 381 : is_valid_mailbox (const char *name)
170 : {
171 381 : 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 167 : mailbox_from_userid (const char *userid)
181 : {
182 : const char *s, *s_end;
183 : size_t len;
184 167 : char *result = NULL;
185 :
186 167 : s = strchr (userid, '<');
187 167 : if (s)
188 : {
189 : /* Seems to be a standard user id. */
190 35 : s++;
191 35 : s_end = strchr (s, '>');
192 35 : if (s_end && s_end > s)
193 : {
194 32 : len = s_end - s;
195 32 : result = xtrymalloc (len + 1);
196 32 : if (!result)
197 0 : return NULL; /* Ooops - out of core. */
198 32 : strncpy (result, s, len);
199 32 : 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 64 : if (string_count_chr (result, '@') != 1 /* Need exactly one '@. */
203 30 : || *result == '@' /* local-part missing. */
204 29 : || result[len-1] == '@' /* domain missing. */
205 29 : || result[len-1] == '.' /* ends with a dot. */
206 27 : || string_has_ctrl_or_space (result)
207 26 : || 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 132 : 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 131 : errno = EINVAL;
228 :
229 167 : 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 0 : is_valid_user_id (const char *uid)
238 : {
239 0 : if (!uid || !*uid)
240 0 : return 0;
241 :
242 0 : return 1;
243 : }
|