NEWS: v14.8.13 ("Cyrustrupidae")
[s-mailx.git] / ssl.c
blobcd64d63eae7a0c767f18f4b660edc53ba55bc6f5
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 - 2015 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.
39 #undef n_FILE
40 #define n_FILE ssl
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 EMPTY_FILE()
47 #ifdef HAVE_SSL
48 struct ssl_verify_levels {
49 char const sv_name[8];
50 enum ssl_verify_level sv_level;
53 /* Supported SSL/TLS verification methods: update manual on change! */
54 static struct ssl_verify_levels const _ssl_verify_levels[] = {
55 {"strict", SSL_VERIFY_STRICT},
56 {"ask", SSL_VERIFY_ASK},
57 {"warn", SSL_VERIFY_WARN},
58 {"ignore", SSL_VERIFY_IGNORE}
61 FL void
62 ssl_set_verify_level(struct url const *urlp)
64 size_t i;
65 char *cp;
66 NYD_ENTER;
68 ssl_verify_level = SSL_VERIFY_ASK;
69 cp = xok_vlook(ssl_verify, urlp, OXM_ALL);
71 if (cp != NULL) {
72 for (i = 0; i < NELEM(_ssl_verify_levels); ++i)
73 if (!asccasecmp(_ssl_verify_levels[i].sv_name, cp)) {
74 ssl_verify_level = _ssl_verify_levels[i].sv_level;
75 goto jleave;
77 n_err(_("Invalid value of *ssl-verify*: %s\n"), cp);
79 jleave:
80 NYD_LEAVE;
83 FL enum okay
84 ssl_verify_decide(void)
86 enum okay rv = STOP;
87 NYD_ENTER;
89 switch (ssl_verify_level) {
90 case SSL_VERIFY_STRICT:
91 rv = STOP;
92 break;
93 case SSL_VERIFY_ASK:
94 rv = getapproval(NULL, FAL0) ? OKAY : STOP;
95 break;
96 case SSL_VERIFY_WARN:
97 case SSL_VERIFY_IGNORE:
98 rv = OKAY;
99 break;
101 NYD_LEAVE;
102 return rv;
105 FL enum okay
106 smime_split(FILE *ip, FILE **hp, FILE **bp, long xcount, int keep)
108 struct myline {
109 struct myline *ml_next;
110 size_t ml_len;
111 char ml_buf[VFIELD_SIZE(0)];
112 } *head, *tail;
113 char *buf;
114 size_t bufsize, buflen, cnt;
115 int c;
116 enum okay rv = STOP;
117 NYD_ENTER;
119 if ((*hp = Ftmp(NULL, "smimeh", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
120 NULL)
121 goto jetmp;
122 if ((*bp = Ftmp(NULL, "smimeb", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
123 NULL) {
124 Fclose(*hp);
125 jetmp:
126 n_perr(_("tempfile"), 0);
127 goto jleave;
130 head = tail = NULL;
131 buf = smalloc(bufsize = LINESIZE);
132 cnt = (xcount < 0) ? fsize(ip) : xcount;
134 while (fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0) != NULL &&
135 *buf != '\n') {
136 if (!ascncasecmp(buf, "content-", 8)) {
137 if (keep)
138 fputs("X-Encoded-", *hp);
139 for (;;) {
140 struct myline *ml = smalloc(sizeof *ml -
141 VFIELD_SIZEOF(struct myline, ml_buf) + buflen +1);
142 if (tail != NULL)
143 tail->ml_next = ml;
144 else
145 head = ml;
146 tail = ml;
147 ml->ml_next = NULL;
148 ml->ml_len = buflen;
149 memcpy(ml->ml_buf, buf, buflen +1);
150 if (keep)
151 fwrite(buf, sizeof *buf, buflen, *hp);
152 c = getc(ip);
153 ungetc(c, ip);
154 if (!blankchar(c))
155 break;
156 fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0);
158 continue;
160 fwrite(buf, sizeof *buf, buflen, *hp);
162 fflush_rewind(*hp);
164 while (head != NULL) {
165 fwrite(head->ml_buf, sizeof *head->ml_buf, head->ml_len, *bp);
166 tail = head;
167 head = head->ml_next;
168 free(tail);
170 putc('\n', *bp);
171 while (fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0) != NULL)
172 fwrite(buf, sizeof *buf, buflen, *bp);
173 fflush_rewind(*bp);
175 free(buf);
176 rv = OKAY;
177 jleave:
178 NYD_LEAVE;
179 return rv;
182 FL FILE *
183 smime_sign_assemble(FILE *hp, FILE *bp, FILE *sp, char const *message_digest)
185 char *boundary;
186 int c, lastc = EOF;
187 FILE *op;
188 NYD_ENTER;
190 if ((op = Ftmp(NULL, "smimea", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
191 NULL) {
192 n_perr(_("tempfile"), 0);
193 goto jleave;
196 while ((c = getc(hp)) != EOF) {
197 if (c == '\n' && lastc == '\n')
198 break;
199 putc(c, op);
200 lastc = c;
203 boundary = mime_param_boundary_create();
204 fprintf(op, "Content-Type: multipart/signed;\n"
205 " protocol=\"application/pkcs7-signature\"; micalg=%s;\n"
206 " boundary=\"%s\"\n\n", message_digest, boundary);
207 fprintf(op, "This is a S/MIME signed message.\n\n--%s\n", boundary);
208 while ((c = getc(bp)) != EOF)
209 putc(c, op);
211 fprintf(op, "\n--%s\n", boundary);
212 fputs("Content-Type: application/pkcs7-signature; name=\"smime.p7s\"\n"
213 "Content-Transfer-Encoding: base64\n"
214 "Content-Disposition: attachment; filename=\"smime.p7s\"\n\n", op);
215 while ((c = getc(sp)) != EOF) {
216 if (c == '-') {
217 while ((c = getc(sp)) != EOF && c != '\n');
218 continue;
220 putc(c, op);
223 fprintf(op, "\n--%s--\n", boundary);
225 Fclose(hp);
226 Fclose(bp);
227 Fclose(sp);
229 fflush(op);
230 if (ferror(op)) {
231 n_perr(_("signed output data"), 0);
232 Fclose(op);
233 op = NULL;
234 goto jleave;
236 rewind(op);
237 jleave:
238 NYD_LEAVE;
239 return op;
242 FL FILE *
243 smime_encrypt_assemble(FILE *hp, FILE *yp)
245 FILE *op;
246 int c, lastc = EOF;
247 NYD_ENTER;
249 if ((op = Ftmp(NULL, "smimee", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
250 NULL) {
251 n_perr(_("tempfile"), 0);
252 goto jleave;
255 while ((c = getc(hp)) != EOF) {
256 if (c == '\n' && lastc == '\n')
257 break;
258 putc(c, op);
259 lastc = c;
262 fprintf(op, "Content-Type: application/pkcs7-mime; name=\"smime.p7m\"\n"
263 "Content-Transfer-Encoding: base64\n"
264 "Content-Disposition: attachment; filename=\"smime.p7m\"\n\n");
265 while ((c = getc(yp)) != EOF) {
266 if (c == '-') {
267 while ((c = getc(yp)) != EOF && c != '\n');
268 continue;
270 putc(c, op);
273 Fclose(hp);
274 Fclose(yp);
276 fflush(op);
277 if (ferror(op)) {
278 n_perr(_("encrypted output data"), 0);
279 Fclose(op);
280 op = NULL;
281 goto jleave;
283 rewind(op);
284 jleave:
285 NYD_LEAVE;
286 return op;
289 FL struct message *
290 smime_decrypt_assemble(struct message *m, FILE *hp, FILE *bp)
292 ui32_t lastnl = 0;
293 int binary = 0;
294 char *buf = NULL;
295 size_t bufsize = 0, buflen, cnt;
296 long lines = 0, octets = 0;
297 struct message *x;
298 off_t offset;
299 NYD_ENTER;
301 x = salloc(sizeof *x);
302 *x = *m;
303 fflush(mb.mb_otf);
304 fseek(mb.mb_otf, 0L, SEEK_END);
305 offset = ftell(mb.mb_otf);
307 cnt = fsize(hp);
308 while (fgetline(&buf, &bufsize, &cnt, &buflen, hp, 0) != NULL) {
309 char const *cp;
310 if (buf[0] == '\n')
311 break;
312 if ((cp = thisfield(buf, "content-transfer-encoding")) != NULL)
313 if (!ascncasecmp(cp, "binary", 7))
314 binary = 1;
315 fwrite(buf, sizeof *buf, buflen, mb.mb_otf);
316 octets += buflen;
317 ++lines;
320 { struct time_current save = time_current;
321 time_current_update(&time_current, TRU1);
322 octets += mkdate(mb.mb_otf, "X-Decoding-Date");
323 time_current = save;
325 ++lines;
327 cnt = fsize(bp);
328 while (fgetline(&buf, &bufsize, &cnt, &buflen, bp, 0) != NULL) {
329 lines++;
330 if (!binary && buf[buflen - 1] == '\n' && buf[buflen - 2] == '\r')
331 buf[--buflen - 1] = '\n';
332 fwrite(buf, sizeof *buf, buflen, mb.mb_otf);
333 octets += buflen;
334 if (buf[0] == '\n')
335 ++lastnl;
336 else if (buf[buflen - 1] == '\n')
337 lastnl = 1;
338 else
339 lastnl = 0;
342 while (!binary && lastnl < 2) {
343 putc('\n', mb.mb_otf);
344 ++lines;
345 ++octets;
346 ++lastnl;
349 Fclose(hp);
350 Fclose(bp);
351 free(buf);
353 fflush(mb.mb_otf);
354 if (ferror(mb.mb_otf)) {
355 n_perr(_("decrypted output data"), 0);
356 x = NULL;
357 goto jleave;
359 x->m_size = x->m_xsize = octets;
360 x->m_lines = x->m_xlines = lines;
361 x->m_block = mailx_blockof(offset);
362 x->m_offset = mailx_offsetof(offset);
363 jleave:
364 NYD_LEAVE;
365 return x;
368 FL int
369 c_certsave(void *v)
371 int *ip, *msgvec, val;
372 char *file = NULL, *str = v;
373 FILE *fp;
374 bool_t f;
375 NYD_ENTER;
377 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
378 val = 1;
380 if ((file = laststring(str, &f, TRU1)) == NULL ||
381 (file = file_expand(file)) == NULL) {
382 n_err(_("No file to save certificate given\n"));
383 goto jleave;
386 if (!f) {
387 msgvec[1] = 0;
388 *msgvec = first(0, MMNORM);
389 } else if (getmsglist(str, msgvec, 0) < 0)
390 goto jleave;
391 if (*msgvec == 0) {
392 if (pstate & PS_HOOK_MASK)
393 val = 0;
394 else
395 n_err(_("No applicable messages\n"));
396 goto jleave;
399 if ((fp = Fopen(file, "a")) == NULL) {
400 n_perr(file, 0);
401 goto jleave;
403 for (val = 0, ip = msgvec;
404 *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount); ++ip)
405 if (smime_certsave(&message[*ip - 1], *ip, fp) != OKAY)
406 val = 1;
407 Fclose(fp);
409 if (val == 0)
410 printf("Certificate(s) saved\n");
411 jleave:
412 NYD_LEAVE;
413 return val;
416 FL enum okay
417 rfc2595_hostname_match(char const *host, char const *pattern)
419 enum okay rv;
420 NYD_ENTER;
422 if (pattern[0] == '*' && pattern[1] == '.') {
423 ++pattern;
424 while (*host && *host != '.')
425 ++host;
427 rv = !asccasecmp(host, pattern) ? OKAY : STOP;
428 NYD_LEAVE;
429 return rv;
431 #endif /* HAVE_SSL */
433 /* s-it-mode */