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