Merge branch 'topic/nyd'
[s-mailx.git] / ssl.c
blobe49073e0c75d7f0f4aafe56866204a403a4d645c
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Generic SSL / S/MIME commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2002
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 EMPTY_FILE(ssl)
45 #ifdef HAVE_SSL
46 struct ssl_verify_levels {
47 char const sv_name[8];
48 enum ssl_verify_level sv_level;
51 /* Supported SSL/TLS verification methods: update manual on change! */
52 static struct ssl_verify_levels const _ssl_verify_levels[] = {
53 {"strict", SSL_VERIFY_STRICT},
54 {"ask", SSL_VERIFY_ASK},
55 {"warn", SSL_VERIFY_WARN},
56 {"ignore", SSL_VERIFY_IGNORE}
59 FL void
60 ssl_set_verify_level(char const *uhp)
62 size_t i;
63 char *cp, *vrvar;
64 NYD_ENTER;
66 ssl_verify_level = SSL_VERIFY_ASK;
68 i = strlen(uhp);
69 vrvar = ac_alloc(11u + i +1);
70 memcpy(vrvar, "ssl-verify-", 11u);
71 memcpy(vrvar + 11u, uhp, i +1);
73 cp = vok_vlook(vrvar);
74 if (cp == NULL) {
75 vrvar[10] = '\0';
76 cp = ok_vlook(ssl_verify);
79 if (cp != NULL) {
80 for (i = 0; i < NELEM(_ssl_verify_levels); ++i)
81 if (!strcmp(_ssl_verify_levels[i].sv_name, cp)) {
82 ssl_verify_level = _ssl_verify_levels[i].sv_level;
83 goto jleave;
85 fprintf(stderr, tr(265, "Invalid value of %s: %s\n"), vrvar, cp);
87 jleave:
88 ac_free(vrvar);
89 NYD_LEAVE;
92 FL enum okay
93 ssl_verify_decide(void)
95 enum okay rv = STOP;
96 NYD_ENTER;
98 switch (ssl_verify_level) {
99 case SSL_VERIFY_STRICT:
100 rv = STOP;
101 break;
102 case SSL_VERIFY_ASK:
103 rv = getapproval(NULL, FAL0) ? OKAY : STOP;
104 break;
105 case SSL_VERIFY_WARN:
106 case SSL_VERIFY_IGNORE:
107 rv = OKAY;
108 break;
110 NYD_LEAVE;
111 return rv;
114 FL char *
115 ssl_method_string(char const *uhp)
117 size_t l;
118 char *cp, *mtvar;
119 NYD_ENTER;
121 l = strlen(uhp);
122 mtvar = ac_alloc(11u + l +1);
123 memcpy(mtvar, "ssl-method-", 11);
124 memcpy(mtvar + 11, uhp, l +1);
125 if ((cp = vok_vlook(mtvar)) == NULL)
126 cp = ok_vlook(ssl_method);
127 ac_free(mtvar);
128 NYD_LEAVE;
129 return cp;
132 FL enum okay
133 smime_split(FILE *ip, FILE **hp, FILE **bp, long xcount, int keep)
135 char *buf, *savedfields/* TODO use struct str or local per-line xy */ = NULL;
136 size_t bufsize, buflen, cnt, savedsize = 0;
137 int c;
138 enum okay rv = STOP;
139 NYD_ENTER;
141 if ((*hp = Ftmp(NULL, "smimeh", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
142 NULL)
143 goto jetmp;
144 if ((*bp = Ftmp(NULL, "smimeb", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
145 NULL) {
146 Fclose(*hp);
147 jetmp:
148 perror("tempfile");
149 goto jleave;
152 buf = smalloc(bufsize = LINESIZE);
153 savedfields = smalloc(savedsize = 1);
154 *savedfields = '\0';
155 if (xcount < 0)
156 cnt = fsize(ip);
157 else
158 cnt = xcount;
160 while (fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0) != NULL &&
161 *buf != '\n') {
162 if (!ascncasecmp(buf, "content-", 8)) {
163 if (keep)
164 fputs("X-Encoded-", *hp);
165 for (;;) {
166 savedsize += buflen;
167 savedfields = srealloc(savedfields, savedsize);
168 strcat(savedfields, buf);
169 if (keep)
170 fwrite(buf, sizeof *buf, buflen, *hp);
171 c = getc(ip);
172 ungetc(c, ip);
173 if (!blankchar(c))
174 break;
175 fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0);
177 continue;
179 fwrite(buf, sizeof *buf, buflen, *hp);
181 fflush_rewind(*hp);
183 fputs(savedfields, *bp);
184 putc('\n', *bp);
185 while (fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0) != NULL)
186 fwrite(buf, sizeof *buf, buflen, *bp);
187 fflush_rewind(*bp);
189 free(savedfields);
190 free(buf);
191 rv = OKAY;
192 jleave:
193 NYD_LEAVE;
194 return rv;
197 FL FILE *
198 smime_sign_assemble(FILE *hp, FILE *bp, FILE *sp)
200 char *boundary;
201 int c, lastc = EOF;
202 FILE *op;
203 NYD_ENTER;
205 if ((op = Ftmp(NULL, "smimea", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
206 NULL) {
207 perror("tempfile");
208 goto jleave;
211 while ((c = getc(hp)) != EOF) {
212 if (c == '\n' && lastc == '\n')
213 break;
214 putc(c, op);
215 lastc = c;
218 boundary = mime_create_boundary();
219 fprintf(op, "Content-Type: multipart/signed;\n"
220 " protocol=\"application/x-pkcs7-signature\"; micalg=sha1;\n"
221 " boundary=\"%s\"\n\n", boundary);
222 fprintf(op, "This is an S/MIME signed message.\n\n--%s\n", boundary);
223 while ((c = getc(bp)) != EOF)
224 putc(c, op);
226 fprintf(op, "\n--%s\n", boundary);
227 fputs("Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"\n"
228 "Content-Transfer-Encoding: base64\n"
229 "Content-Disposition: attachment; filename=\"smime.p7s\"\n\n", op);
230 while ((c = getc(sp)) != EOF) {
231 if (c == '-') {
232 while ((c = getc(sp)) != EOF && c != '\n');
233 continue;
235 putc(c, op);
238 fprintf(op, "\n--%s--\n", boundary);
240 Fclose(hp);
241 Fclose(bp);
242 Fclose(sp);
244 fflush(op);
245 if (ferror(op)) {
246 perror("signed output data");
247 Fclose(op);
248 op = NULL;
249 goto jleave;
251 rewind(op);
252 jleave:
253 NYD_LEAVE;
254 return op;
257 FL FILE *
258 smime_encrypt_assemble(FILE *hp, FILE *yp)
260 FILE *op;
261 int c, lastc = EOF;
262 NYD_ENTER;
264 if ((op = Ftmp(NULL, "smimee", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
265 NULL) {
266 perror("tempfile");
267 goto jleave;
270 while ((c = getc(hp)) != EOF) {
271 if (c == '\n' && lastc == '\n')
272 break;
273 putc(c, op);
274 lastc = c;
277 fprintf(op, "Content-Type: application/x-pkcs7-mime; name=\"smime.p7m\"\n"
278 "Content-Transfer-Encoding: base64\n"
279 "Content-Disposition: attachment; filename=\"smime.p7m\"\n\n");
280 while ((c = getc(yp)) != EOF) {
281 if (c == '-') {
282 while ((c = getc(yp)) != EOF && c != '\n');
283 continue;
285 putc(c, op);
288 Fclose(hp);
289 Fclose(yp);
291 fflush(op);
292 if (ferror(op)) {
293 perror("encrypted output data");
294 Fclose(op);
295 op = NULL;
296 goto jleave;
298 rewind(op);
299 jleave:
300 NYD_LEAVE;
301 return op;
304 FL struct message *
305 smime_decrypt_assemble(struct message *m, FILE *hp, FILE *bp)
307 ui32_t lastnl = 0;
308 int binary = 0;
309 char *buf = NULL;
310 size_t bufsize = 0, buflen, cnt;
311 long lines = 0, octets = 0;
312 struct message *x;
313 off_t offset;
314 NYD_ENTER;
316 x = salloc(sizeof *x);
317 *x = *m;
318 fflush(mb.mb_otf);
319 fseek(mb.mb_otf, 0L, SEEK_END);
320 offset = ftell(mb.mb_otf);
322 cnt = fsize(hp);
323 while (fgetline(&buf, &bufsize, &cnt, &buflen, hp, 0) != NULL) {
324 char const *cp;
325 if (buf[0] == '\n')
326 break;
327 if ((cp = thisfield(buf, "content-transfer-encoding")) != NULL)
328 if (!ascncasecmp(cp, "binary", 7))
329 binary = 1;
330 fwrite(buf, sizeof *buf, buflen, mb.mb_otf);
331 octets += buflen;
332 ++lines;
335 octets += mkdate(mb.mb_otf, "X-Decoding-Date");
336 ++lines;
338 cnt = fsize(bp);
339 while (fgetline(&buf, &bufsize, &cnt, &buflen, bp, 0) != NULL) {
340 lines++;
341 if (!binary && buf[buflen - 1] == '\n' && buf[buflen - 2] == '\r')
342 buf[--buflen - 1] = '\n';
343 fwrite(buf, sizeof *buf, buflen, mb.mb_otf);
344 octets += buflen;
345 if (buf[0] == '\n')
346 ++lastnl;
347 else if (buf[buflen - 1] == '\n')
348 lastnl = 1;
349 else
350 lastnl = 0;
353 while (!binary && lastnl < 2) {
354 putc('\n', mb.mb_otf);
355 ++lines;
356 ++octets;
357 ++lastnl;
360 Fclose(hp);
361 Fclose(bp);
362 free(buf);
364 fflush(mb.mb_otf);
365 if (ferror(mb.mb_otf)) {
366 perror("decrypted output data");
367 x = NULL;
368 goto jleave;
370 x->m_size = x->m_xsize = octets;
371 x->m_lines = x->m_xlines = lines;
372 x->m_block = mailx_blockof(offset);
373 x->m_offset = mailx_offsetof(offset);
374 jleave:
375 NYD_LEAVE;
376 return x;
379 FL int
380 ccertsave(void *v)
382 int *ip, *msgvec, val;
383 char *file = NULL, *str = v;
384 FILE *fp;
385 bool_t f;
386 NYD_ENTER;
388 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
389 val = 1;
391 if ((file = laststring(str, &f, 1)) == NULL) {
392 fprintf(stderr, "No file to save certificate given.\n");
393 goto jleave;
395 if (!f) {
396 *msgvec = first(0, MMNORM);
397 if (*msgvec == 0) {
398 if (inhook)
399 val = 0;
400 else
401 fprintf(stderr, "No messages to get certificates from.\n");
402 goto jleave;
404 msgvec[1] = 0;
405 } else if (getmsglist(str, msgvec, 0) < 0)
406 goto jleave;
408 if (*msgvec == 0) {
409 if (inhook)
410 val = 0;
411 else
412 fprintf(stderr, "No applicable messages.\n");
413 goto jleave;
416 if ((fp = Fopen(file, "a")) == NULL) {
417 perror(file);
418 goto jleave;
420 for (val = 0, ip = msgvec;
421 *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount); ++ip)
422 if (smime_certsave(&message[*ip - 1], *ip, fp) != OKAY)
423 val = 1;
424 Fclose(fp);
426 if (val == 0)
427 printf("Certificate(s) saved.\n");
428 jleave:
429 NYD_LEAVE;
430 return val;
433 FL enum okay
434 rfc2595_hostname_match(char const *host, char const *pattern)
436 enum okay rv;
437 NYD_ENTER;
439 if (pattern[0] == '*' && pattern[1] == '.') {
440 ++pattern;
441 while (*host && *host != '.')
442 ++host;
444 rv = !asccasecmp(host, pattern) ? OKAY : STOP;
445 NYD_LEAVE;
446 return rv;
448 #endif /* HAVE_SSL */
450 /* vim:set fenc=utf-8:s-it-mode */