main.c: set command line options immediately..
[s-mailx.git] / ssl.c
blob3fe5c9b70d1eb1b5aa74a8f2b527e8ea50f23990
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, tr(265, "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 char *buf, *savedfields/* TODO use struct str or local per-line xy */ = NULL;
139 size_t bufsize, buflen, cnt, savedsize = 0;
140 int c;
141 enum okay rv = STOP;
142 NYD_ENTER;
144 if ((*hp = Ftmp(NULL, "smimeh", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
145 NULL)
146 goto jetmp;
147 if ((*bp = Ftmp(NULL, "smimeb", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
148 NULL) {
149 Fclose(*hp);
150 jetmp:
151 perror("tempfile");
152 goto jleave;
155 buf = smalloc(bufsize = LINESIZE);
156 savedfields = smalloc(savedsize = 1);
157 *savedfields = '\0';
158 if (xcount < 0)
159 cnt = fsize(ip);
160 else
161 cnt = xcount;
163 while (fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0) != NULL &&
164 *buf != '\n') {
165 if (!ascncasecmp(buf, "content-", 8)) {
166 if (keep)
167 fputs("X-Encoded-", *hp);
168 for (;;) {
169 savedsize += buflen;
170 savedfields = srealloc(savedfields, savedsize);
171 strcat(savedfields, buf);
172 if (keep)
173 fwrite(buf, sizeof *buf, buflen, *hp);
174 c = getc(ip);
175 ungetc(c, ip);
176 if (!blankchar(c))
177 break;
178 fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0);
180 continue;
182 fwrite(buf, sizeof *buf, buflen, *hp);
184 fflush_rewind(*hp);
186 fputs(savedfields, *bp);
187 putc('\n', *bp);
188 while (fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0) != NULL)
189 fwrite(buf, sizeof *buf, buflen, *bp);
190 fflush_rewind(*bp);
192 free(savedfields);
193 free(buf);
194 rv = OKAY;
195 jleave:
196 NYD_LEAVE;
197 return rv;
200 FL FILE *
201 smime_sign_assemble(FILE *hp, FILE *bp, FILE *sp)
203 char *boundary;
204 int c, lastc = EOF;
205 FILE *op;
206 NYD_ENTER;
208 if ((op = Ftmp(NULL, "smimea", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
209 NULL) {
210 perror("tempfile");
211 goto jleave;
214 while ((c = getc(hp)) != EOF) {
215 if (c == '\n' && lastc == '\n')
216 break;
217 putc(c, op);
218 lastc = c;
221 boundary = mime_create_boundary();
222 fprintf(op, "Content-Type: multipart/signed;\n"
223 " protocol=\"application/x-pkcs7-signature\"; micalg=sha1;\n"
224 " boundary=\"%s\"\n\n", boundary);
225 fprintf(op, "This is an S/MIME signed message.\n\n--%s\n", boundary);
226 while ((c = getc(bp)) != EOF)
227 putc(c, op);
229 fprintf(op, "\n--%s\n", boundary);
230 fputs("Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"\n"
231 "Content-Transfer-Encoding: base64\n"
232 "Content-Disposition: attachment; filename=\"smime.p7s\"\n\n", op);
233 while ((c = getc(sp)) != EOF) {
234 if (c == '-') {
235 while ((c = getc(sp)) != EOF && c != '\n');
236 continue;
238 putc(c, op);
241 fprintf(op, "\n--%s--\n", boundary);
243 Fclose(hp);
244 Fclose(bp);
245 Fclose(sp);
247 fflush(op);
248 if (ferror(op)) {
249 perror("signed output data");
250 Fclose(op);
251 op = NULL;
252 goto jleave;
254 rewind(op);
255 jleave:
256 NYD_LEAVE;
257 return op;
260 FL FILE *
261 smime_encrypt_assemble(FILE *hp, FILE *yp)
263 FILE *op;
264 int c, lastc = EOF;
265 NYD_ENTER;
267 if ((op = Ftmp(NULL, "smimee", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
268 NULL) {
269 perror("tempfile");
270 goto jleave;
273 while ((c = getc(hp)) != EOF) {
274 if (c == '\n' && lastc == '\n')
275 break;
276 putc(c, op);
277 lastc = c;
280 fprintf(op, "Content-Type: application/x-pkcs7-mime; name=\"smime.p7m\"\n"
281 "Content-Transfer-Encoding: base64\n"
282 "Content-Disposition: attachment; filename=\"smime.p7m\"\n\n");
283 while ((c = getc(yp)) != EOF) {
284 if (c == '-') {
285 while ((c = getc(yp)) != EOF && c != '\n');
286 continue;
288 putc(c, op);
291 Fclose(hp);
292 Fclose(yp);
294 fflush(op);
295 if (ferror(op)) {
296 perror("encrypted output data");
297 Fclose(op);
298 op = NULL;
299 goto jleave;
301 rewind(op);
302 jleave:
303 NYD_LEAVE;
304 return op;
307 FL struct message *
308 smime_decrypt_assemble(struct message *m, FILE *hp, FILE *bp)
310 ui32_t lastnl = 0;
311 int binary = 0;
312 char *buf = NULL;
313 size_t bufsize = 0, buflen, cnt;
314 long lines = 0, octets = 0;
315 struct message *x;
316 off_t offset;
317 NYD_ENTER;
319 x = salloc(sizeof *x);
320 *x = *m;
321 fflush(mb.mb_otf);
322 fseek(mb.mb_otf, 0L, SEEK_END);
323 offset = ftell(mb.mb_otf);
325 cnt = fsize(hp);
326 while (fgetline(&buf, &bufsize, &cnt, &buflen, hp, 0) != NULL) {
327 char const *cp;
328 if (buf[0] == '\n')
329 break;
330 if ((cp = thisfield(buf, "content-transfer-encoding")) != NULL)
331 if (!ascncasecmp(cp, "binary", 7))
332 binary = 1;
333 fwrite(buf, sizeof *buf, buflen, mb.mb_otf);
334 octets += buflen;
335 ++lines;
338 { struct time_current save = time_current;
339 time_current_update(&time_current, TRU1);
340 octets += mkdate(mb.mb_otf, "X-Decoding-Date");
341 time_current = save;
343 ++lines;
345 cnt = fsize(bp);
346 while (fgetline(&buf, &bufsize, &cnt, &buflen, bp, 0) != NULL) {
347 lines++;
348 if (!binary && buf[buflen - 1] == '\n' && buf[buflen - 2] == '\r')
349 buf[--buflen - 1] = '\n';
350 fwrite(buf, sizeof *buf, buflen, mb.mb_otf);
351 octets += buflen;
352 if (buf[0] == '\n')
353 ++lastnl;
354 else if (buf[buflen - 1] == '\n')
355 lastnl = 1;
356 else
357 lastnl = 0;
360 while (!binary && lastnl < 2) {
361 putc('\n', mb.mb_otf);
362 ++lines;
363 ++octets;
364 ++lastnl;
367 Fclose(hp);
368 Fclose(bp);
369 free(buf);
371 fflush(mb.mb_otf);
372 if (ferror(mb.mb_otf)) {
373 perror("decrypted output data");
374 x = NULL;
375 goto jleave;
377 x->m_size = x->m_xsize = octets;
378 x->m_lines = x->m_xlines = lines;
379 x->m_block = mailx_blockof(offset);
380 x->m_offset = mailx_offsetof(offset);
381 jleave:
382 NYD_LEAVE;
383 return x;
386 FL int
387 c_certsave(void *v)
389 int *ip, *msgvec, val;
390 char *file = NULL, *str = v;
391 FILE *fp;
392 bool_t f;
393 NYD_ENTER;
395 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
396 val = 1;
398 if ((file = laststring(str, &f, 1)) == NULL ||
399 (file = file_expand(file)) == NULL) {
400 fprintf(stderr, "No file to save certificate given.\n");
401 goto jleave;
404 if (!f) {
405 msgvec[1] = 0;
406 *msgvec = first(0, MMNORM);
407 } else
408 getmsglist(str, msgvec, 0);
409 if (*msgvec == 0) {
410 if (inhook)
411 val = 0;
412 else
413 fprintf(stderr, "No applicable messages.\n");
414 goto jleave;
417 if ((fp = Fopen(file, "a")) == NULL) {
418 perror(file);
419 goto jleave;
421 for (val = 0, ip = msgvec;
422 *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount); ++ip)
423 if (smime_certsave(&message[*ip - 1], *ip, fp) != OKAY)
424 val = 1;
425 Fclose(fp);
427 if (val == 0)
428 printf("Certificate(s) saved.\n");
429 jleave:
430 NYD_LEAVE;
431 return val;
434 FL enum okay
435 rfc2595_hostname_match(char const *host, char const *pattern)
437 enum okay rv;
438 NYD_ENTER;
440 if (pattern[0] == '*' && pattern[1] == '.') {
441 ++pattern;
442 while (*host && *host != '.')
443 ++host;
445 rv = !asccasecmp(host, pattern) ? OKAY : STOP;
446 NYD_LEAVE;
447 return rv;
449 #endif /* HAVE_SSL */
451 /* vim:set fenc=utf-8:s-it-mode */