Fix QP encoding canary violation (Peter Hofmann)..
[s-mailx.git] / ssl.c
blobcf87596081a4bd0f2b3840b70e74a09577c8cc7e
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);
71 memcpy(vrvar, "ssl-verify-", 11);
72 memcpy(vrvar + 11, uhp, i +1);
73 cp = vok_vlook(vrvar);
75 if (cp == NULL) {
76 vrvar[10] = '\0';
77 cp = ok_vlook(ssl_verify);
80 if (cp != NULL) {
81 for (i = 0; i < NELEM(_ssl_verify_levels); ++i)
82 if (!strcmp(_ssl_verify_levels[i].sv_name, cp)) {
83 ssl_verify_level = _ssl_verify_levels[i].sv_level;
84 goto jleave;
86 fprintf(stderr, _("Invalid value of %s: %s\n"), vrvar, cp);
88 jleave:
89 ac_free(vrvar);
90 NYD_LEAVE;
93 FL enum okay
94 ssl_verify_decide(void)
96 enum okay rv = STOP;
97 NYD_ENTER;
99 switch (ssl_verify_level) {
100 case SSL_VERIFY_STRICT:
101 rv = STOP;
102 break;
103 case SSL_VERIFY_ASK:
104 rv = getapproval(NULL, FAL0) ? OKAY : STOP;
105 break;
106 case SSL_VERIFY_WARN:
107 case SSL_VERIFY_IGNORE:
108 rv = OKAY;
109 break;
111 NYD_LEAVE;
112 return rv;
115 FL char *
116 ssl_method_string(char const *uhp)
118 size_t l;
119 char *cp, *mtvar;
120 NYD_ENTER;
122 l = strlen(uhp);
123 mtvar = ac_alloc(11 + l +1);
125 memcpy(mtvar, "ssl-method-", 11);
126 memcpy(mtvar + 11, uhp, l +1);
127 if ((cp = vok_vlook(mtvar)) == NULL)
128 cp = ok_vlook(ssl_method);
130 ac_free(mtvar);
131 NYD_LEAVE;
132 return cp;
135 FL enum okay
136 smime_split(FILE *ip, FILE **hp, FILE **bp, long xcount, int keep)
138 struct myline {
139 struct myline *ml_next;
140 size_t ml_len;
141 char ml_buf[VFIELD_SIZE(sizeof(uiz_t))];
142 } *head, *tail;
143 char *buf;
144 size_t bufsize, buflen, cnt;
145 int c;
146 enum okay rv = STOP;
147 NYD_ENTER;
149 if ((*hp = Ftmp(NULL, "smimeh", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
150 NULL)
151 goto jetmp;
152 if ((*bp = Ftmp(NULL, "smimeb", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
153 NULL) {
154 Fclose(*hp);
155 jetmp:
156 perror("tempfile");
157 goto jleave;
160 head = tail = NULL;
161 buf = smalloc(bufsize = LINESIZE);
162 cnt = (xcount < 0) ? fsize(ip) : xcount;
164 while (fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0) != NULL &&
165 *buf != '\n') {
166 if (!ascncasecmp(buf, "content-", 8)) {
167 if (keep)
168 fputs("X-Encoded-", *hp);
169 for (;;) {
170 struct myline *ml = smalloc(sizeof *ml -
171 VFIELD_SIZEOF(struct myline, ml_buf) + buflen +1);
172 if (tail != NULL)
173 tail->ml_next = ml;
174 else
175 head = ml;
176 tail = ml;
177 ml->ml_next = NULL;
178 ml->ml_len = buflen;
179 memcpy(ml->ml_buf, buf, buflen +1);
180 if (keep)
181 fwrite(buf, sizeof *buf, buflen, *hp);
182 c = getc(ip);
183 ungetc(c, ip);
184 if (!blankchar(c))
185 break;
186 fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0);
188 continue;
190 fwrite(buf, sizeof *buf, buflen, *hp);
192 fflush_rewind(*hp);
194 while (head != NULL) {
195 fwrite(head->ml_buf, sizeof *head->ml_buf, head->ml_len, *bp);
196 tail = head;
197 head = head->ml_next;
198 free(tail);
200 putc('\n', *bp);
201 while (fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0) != NULL)
202 fwrite(buf, sizeof *buf, buflen, *bp);
203 fflush_rewind(*bp);
205 free(buf);
206 rv = OKAY;
207 jleave:
208 NYD_LEAVE;
209 return rv;
212 FL FILE *
213 smime_sign_assemble(FILE *hp, FILE *bp, FILE *sp)
215 char *boundary;
216 int c, lastc = EOF;
217 FILE *op;
218 NYD_ENTER;
220 if ((op = Ftmp(NULL, "smimea", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
221 NULL) {
222 perror("tempfile");
223 goto jleave;
226 while ((c = getc(hp)) != EOF) {
227 if (c == '\n' && lastc == '\n')
228 break;
229 putc(c, op);
230 lastc = c;
233 boundary = mime_create_boundary();
234 fprintf(op, "Content-Type: multipart/signed;\n"
235 " protocol=\"application/x-pkcs7-signature\"; micalg=sha1;\n"
236 " boundary=\"%s\"\n\n", boundary);
237 fprintf(op, "This is an S/MIME signed message.\n\n--%s\n", boundary);
238 while ((c = getc(bp)) != EOF)
239 putc(c, op);
241 fprintf(op, "\n--%s\n", boundary);
242 fputs("Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"\n"
243 "Content-Transfer-Encoding: base64\n"
244 "Content-Disposition: attachment; filename=\"smime.p7s\"\n\n", op);
245 while ((c = getc(sp)) != EOF) {
246 if (c == '-') {
247 while ((c = getc(sp)) != EOF && c != '\n');
248 continue;
250 putc(c, op);
253 fprintf(op, "\n--%s--\n", boundary);
255 Fclose(hp);
256 Fclose(bp);
257 Fclose(sp);
259 fflush(op);
260 if (ferror(op)) {
261 perror("signed output data");
262 Fclose(op);
263 op = NULL;
264 goto jleave;
266 rewind(op);
267 jleave:
268 NYD_LEAVE;
269 return op;
272 FL FILE *
273 smime_encrypt_assemble(FILE *hp, FILE *yp)
275 FILE *op;
276 int c, lastc = EOF;
277 NYD_ENTER;
279 if ((op = Ftmp(NULL, "smimee", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
280 NULL) {
281 perror("tempfile");
282 goto jleave;
285 while ((c = getc(hp)) != EOF) {
286 if (c == '\n' && lastc == '\n')
287 break;
288 putc(c, op);
289 lastc = c;
292 fprintf(op, "Content-Type: application/x-pkcs7-mime; name=\"smime.p7m\"\n"
293 "Content-Transfer-Encoding: base64\n"
294 "Content-Disposition: attachment; filename=\"smime.p7m\"\n\n");
295 while ((c = getc(yp)) != EOF) {
296 if (c == '-') {
297 while ((c = getc(yp)) != EOF && c != '\n');
298 continue;
300 putc(c, op);
303 Fclose(hp);
304 Fclose(yp);
306 fflush(op);
307 if (ferror(op)) {
308 perror("encrypted output data");
309 Fclose(op);
310 op = NULL;
311 goto jleave;
313 rewind(op);
314 jleave:
315 NYD_LEAVE;
316 return op;
319 FL struct message *
320 smime_decrypt_assemble(struct message *m, FILE *hp, FILE *bp)
322 ui32_t lastnl = 0;
323 int binary = 0;
324 char *buf = NULL;
325 size_t bufsize = 0, buflen, cnt;
326 long lines = 0, octets = 0;
327 struct message *x;
328 off_t offset;
329 NYD_ENTER;
331 x = salloc(sizeof *x);
332 *x = *m;
333 fflush(mb.mb_otf);
334 fseek(mb.mb_otf, 0L, SEEK_END);
335 offset = ftell(mb.mb_otf);
337 cnt = fsize(hp);
338 while (fgetline(&buf, &bufsize, &cnt, &buflen, hp, 0) != NULL) {
339 char const *cp;
340 if (buf[0] == '\n')
341 break;
342 if ((cp = thisfield(buf, "content-transfer-encoding")) != NULL)
343 if (!ascncasecmp(cp, "binary", 7))
344 binary = 1;
345 fwrite(buf, sizeof *buf, buflen, mb.mb_otf);
346 octets += buflen;
347 ++lines;
350 { struct time_current save = time_current;
351 time_current_update(&time_current, TRU1);
352 octets += mkdate(mb.mb_otf, "X-Decoding-Date");
353 time_current = save;
355 ++lines;
357 cnt = fsize(bp);
358 while (fgetline(&buf, &bufsize, &cnt, &buflen, bp, 0) != NULL) {
359 lines++;
360 if (!binary && buf[buflen - 1] == '\n' && buf[buflen - 2] == '\r')
361 buf[--buflen - 1] = '\n';
362 fwrite(buf, sizeof *buf, buflen, mb.mb_otf);
363 octets += buflen;
364 if (buf[0] == '\n')
365 ++lastnl;
366 else if (buf[buflen - 1] == '\n')
367 lastnl = 1;
368 else
369 lastnl = 0;
372 while (!binary && lastnl < 2) {
373 putc('\n', mb.mb_otf);
374 ++lines;
375 ++octets;
376 ++lastnl;
379 Fclose(hp);
380 Fclose(bp);
381 free(buf);
383 fflush(mb.mb_otf);
384 if (ferror(mb.mb_otf)) {
385 perror("decrypted output data");
386 x = NULL;
387 goto jleave;
389 x->m_size = x->m_xsize = octets;
390 x->m_lines = x->m_xlines = lines;
391 x->m_block = mailx_blockof(offset);
392 x->m_offset = mailx_offsetof(offset);
393 jleave:
394 NYD_LEAVE;
395 return x;
398 FL int
399 c_certsave(void *v)
401 int *ip, *msgvec, val;
402 char *file = NULL, *str = v;
403 FILE *fp;
404 bool_t f;
405 NYD_ENTER;
407 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
408 val = 1;
410 if ((file = laststring(str, &f, TRU1)) == NULL ||
411 (file = file_expand(file)) == NULL) {
412 fprintf(stderr, "No file to save certificate given.\n");
413 goto jleave;
416 if (!f) {
417 msgvec[1] = 0;
418 *msgvec = first(0, MMNORM);
419 } else
420 getmsglist(str, msgvec, 0);
421 if (*msgvec == 0) {
422 if (inhook)
423 val = 0;
424 else
425 fprintf(stderr, "No applicable messages.\n");
426 goto jleave;
429 if ((fp = Fopen(file, "a")) == NULL) {
430 perror(file);
431 goto jleave;
433 for (val = 0, ip = msgvec;
434 *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount); ++ip)
435 if (smime_certsave(&message[*ip - 1], *ip, fp) != OKAY)
436 val = 1;
437 Fclose(fp);
439 if (val == 0)
440 printf("Certificate(s) saved.\n");
441 jleave:
442 NYD_LEAVE;
443 return val;
446 FL enum okay
447 rfc2595_hostname_match(char const *host, char const *pattern)
449 enum okay rv;
450 NYD_ENTER;
452 if (pattern[0] == '*' && pattern[1] == '.') {
453 ++pattern;
454 while (*host && *host != '.')
455 ++host;
457 rv = !asccasecmp(host, pattern) ? OKAY : STOP;
458 NYD_LEAVE;
459 return rv;
461 #endif /* HAVE_SSL */
463 /* s-it-mode */