Add ~I command escape (like ~i, but do not add newline)
[s-mailx.git] / ssl.c
blobe20f7a4d5455cfe9613c408f7934b0ed1acc59c5
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 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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 < n_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[n_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)) == NULL)
120 goto jetmp;
121 if ((*bp = Ftmp(NULL, "smimeb", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
122 Fclose(*hp);
123 jetmp:
124 n_perr(_("tempfile"), 0);
125 goto jleave;
128 head = tail = NULL;
129 buf = smalloc(bufsize = LINESIZE);
130 cnt = (xcount < 0) ? fsize(ip) : xcount;
132 while (fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0) != NULL &&
133 *buf != '\n') {
134 if (!ascncasecmp(buf, "content-", 8)) {
135 if (keep)
136 fputs("X-Encoded-", *hp);
137 for (;;) {
138 struct myline *ml = smalloc(n_VSTRUCT_SIZEOF(struct myline, ml_buf
139 ) + buflen +1);
140 if (tail != NULL)
141 tail->ml_next = ml;
142 else
143 head = ml;
144 tail = ml;
145 ml->ml_next = NULL;
146 ml->ml_len = buflen;
147 memcpy(ml->ml_buf, buf, buflen +1);
148 if (keep)
149 fwrite(buf, sizeof *buf, buflen, *hp);
150 c = getc(ip);
151 ungetc(c, ip);
152 if (!blankchar(c))
153 break;
154 fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0);
156 continue;
158 fwrite(buf, sizeof *buf, buflen, *hp);
160 fflush_rewind(*hp);
162 while (head != NULL) {
163 fwrite(head->ml_buf, sizeof *head->ml_buf, head->ml_len, *bp);
164 tail = head;
165 head = head->ml_next;
166 free(tail);
168 putc('\n', *bp);
169 while (fgetline(&buf, &bufsize, &cnt, &buflen, ip, 0) != NULL)
170 fwrite(buf, sizeof *buf, buflen, *bp);
171 fflush_rewind(*bp);
173 free(buf);
174 rv = OKAY;
175 jleave:
176 NYD_LEAVE;
177 return rv;
180 FL FILE *
181 smime_sign_assemble(FILE *hp, FILE *bp, FILE *sp, char const *message_digest)
183 char *boundary;
184 int c, lastc = EOF;
185 FILE *op;
186 NYD_ENTER;
188 if ((op = Ftmp(NULL, "smimea", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL) {
189 n_perr(_("tempfile"), 0);
190 goto jleave;
193 while ((c = getc(hp)) != EOF) {
194 if (c == '\n' && lastc == '\n')
195 break;
196 putc(c, op);
197 lastc = c;
200 boundary = mime_param_boundary_create();
201 fprintf(op, "Content-Type: multipart/signed;\n"
202 " protocol=\"application/pkcs7-signature\"; micalg=%s;\n"
203 " boundary=\"%s\"\n\n", message_digest, boundary);
204 fprintf(op, "This is a S/MIME signed message.\n\n--%s\n", boundary);
205 while ((c = getc(bp)) != EOF)
206 putc(c, op);
208 fprintf(op, "\n--%s\n", boundary);
209 fputs("Content-Type: application/pkcs7-signature; name=\"smime.p7s\"\n"
210 "Content-Transfer-Encoding: base64\n"
211 "Content-Disposition: attachment; filename=\"smime.p7s\"\n"
212 "Content-Description: S/MIME digital signature\n\n", op);
213 while ((c = getc(sp)) != EOF) {
214 if (c == '-') {
215 while ((c = getc(sp)) != EOF && c != '\n');
216 continue;
218 putc(c, op);
221 fprintf(op, "\n--%s--\n", boundary);
223 Fclose(hp);
224 Fclose(bp);
225 Fclose(sp);
227 fflush(op);
228 if (ferror(op)) {
229 n_perr(_("signed output data"), 0);
230 Fclose(op);
231 op = NULL;
232 goto jleave;
234 rewind(op);
235 jleave:
236 NYD_LEAVE;
237 return op;
240 FL FILE *
241 smime_encrypt_assemble(FILE *hp, FILE *yp)
243 FILE *op;
244 int c, lastc = EOF;
245 NYD_ENTER;
247 if ((op = Ftmp(NULL, "smimee", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL) {
248 n_perr(_("tempfile"), 0);
249 goto jleave;
252 while ((c = getc(hp)) != EOF) {
253 if (c == '\n' && lastc == '\n')
254 break;
255 putc(c, op);
256 lastc = c;
259 fputs("Content-Type: application/pkcs7-mime; name=\"smime.p7m\"\n"
260 "Content-Transfer-Encoding: base64\n"
261 "Content-Disposition: attachment; filename=\"smime.p7m\"\n"
262 "Content-Description: S/MIME encrypted message\n\n", op);
263 while ((c = getc(yp)) != EOF) {
264 if (c == '-') {
265 while ((c = getc(yp)) != EOF && c != '\n');
266 continue;
268 putc(c, op);
271 Fclose(hp);
272 Fclose(yp);
274 fflush(op);
275 if (ferror(op)) {
276 n_perr(_("encrypted output data"), 0);
277 Fclose(op);
278 op = NULL;
279 goto jleave;
281 rewind(op);
282 jleave:
283 NYD_LEAVE;
284 return op;
287 FL struct message *
288 smime_decrypt_assemble(struct message *m, FILE *hp, FILE *bp)
290 ui32_t lastnl = 0;
291 int binary = 0;
292 char *buf = NULL;
293 size_t bufsize = 0, buflen, cnt;
294 long lines = 0, octets = 0;
295 struct message *x;
296 off_t offset;
297 NYD_ENTER;
299 x = salloc(sizeof *x);
300 *x = *m;
301 fflush(mb.mb_otf);
302 fseek(mb.mb_otf, 0L, SEEK_END);
303 offset = ftell(mb.mb_otf);
305 cnt = fsize(hp);
306 while (fgetline(&buf, &bufsize, &cnt, &buflen, hp, 0) != NULL) {
307 char const *cp;
308 if (buf[0] == '\n')
309 break;
310 if ((cp = thisfield(buf, "content-transfer-encoding")) != NULL)
311 if (!ascncasecmp(cp, "binary", 7))
312 binary = 1;
313 fwrite(buf, sizeof *buf, buflen, mb.mb_otf);
314 octets += buflen;
315 ++lines;
318 { struct time_current save = time_current;
319 time_current_update(&time_current, TRU1);
320 octets += mkdate(mb.mb_otf, "X-Decoding-Date");
321 time_current = save;
323 ++lines;
325 cnt = fsize(bp);
326 while (fgetline(&buf, &bufsize, &cnt, &buflen, bp, 0) != NULL) {
327 lines++;
328 if (!binary && buf[buflen - 1] == '\n' && buf[buflen - 2] == '\r')
329 buf[--buflen - 1] = '\n';
330 fwrite(buf, sizeof *buf, buflen, mb.mb_otf);
331 octets += buflen;
332 if (buf[0] == '\n')
333 ++lastnl;
334 else if (buf[buflen - 1] == '\n')
335 lastnl = 1;
336 else
337 lastnl = 0;
340 while (!binary && lastnl < 2) {
341 putc('\n', mb.mb_otf);
342 ++lines;
343 ++octets;
344 ++lastnl;
347 Fclose(hp);
348 Fclose(bp);
349 free(buf);
351 fflush(mb.mb_otf);
352 if (ferror(mb.mb_otf)) {
353 n_perr(_("decrypted output data"), 0);
354 x = NULL;
355 }else{
356 x->m_size = x->m_xsize = octets;
357 x->m_lines = x->m_xlines = lines;
358 x->m_block = mailx_blockof(offset);
359 x->m_offset = mailx_offsetof(offset);
361 NYD_LEAVE;
362 return x;
365 FL int
366 c_certsave(void *v)
368 int *ip, *msgvec, val;
369 char *file = NULL, *str = v;
370 FILE *fp;
371 bool_t f;
372 NYD_ENTER;
374 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
375 val = 1;
377 if ((file = laststring(str, &f, TRU1)) == NULL ||
378 (file = fexpand(file, FEXP_LOCAL | FEXP_NOPROTO)) == NULL) {
379 n_err(_("No file to save certificate given\n"));
380 goto jleave;
383 if (!f) {
384 msgvec[1] = 0;
385 *msgvec = first(0, MMNORM);
386 } else if (getmsglist(str, msgvec, 0) < 0)
387 goto jleave;
388 if (*msgvec == 0) {
389 if (n_pstate & (n_PS_HOOK_MASK | n_PS_ROBOT))
390 val = 0;
391 else
392 n_err(_("No applicable messages\n"));
393 goto jleave;
396 if ((fp = Fopen(file, "a")) == NULL) {
397 n_perr(file, 0);
398 goto jleave;
400 for (val = 0, ip = msgvec;
401 *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount); ++ip)
402 if (smime_certsave(&message[*ip - 1], *ip, fp) != OKAY)
403 val = 1;
404 Fclose(fp);
406 if (val == 0)
407 fprintf(n_stdout, "Certificate(s) saved\n");
408 jleave:
409 NYD_LEAVE;
410 return val;
413 FL enum okay
414 rfc2595_hostname_match(char const *host, char const *pattern)
416 enum okay rv;
417 NYD_ENTER;
419 if (pattern[0] == '*' && pattern[1] == '.') {
420 ++pattern;
421 while (*host && *host != '.')
422 ++host;
424 rv = !asccasecmp(host, pattern) ? OKAY : STOP;
425 NYD_LEAVE;
426 return rv;
428 #endif /* HAVE_SSL */
430 /* s-it-mode */