1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Generic TLS / S/MIME commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-4-Clause TODO ISC (is taken from book!)
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 #ifndef HAVE_AMALGAMATION
49 struct a_tls_verify_levels
{
50 char const tv_name
[8];
51 enum n_tls_verify_level tv_level
;
54 /* Supported SSL/TLS verification methods: update manual on change! */
55 static struct a_tls_verify_levels
const a_tls_verify_levels
[] = {
56 {"strict", n_TLS_VERIFY_STRICT
},
57 {"ask", n_TLS_VERIFY_ASK
},
58 {"warn", n_TLS_VERIFY_WARN
},
59 {"ignore", n_TLS_VERIFY_IGNORE
}
63 n_tls_set_verify_level(struct url
const *urlp
){
68 n_tls_verify_level
= n_TLS_VERIFY_ASK
;
70 if((cp
= xok_vlook(tls_verify
, urlp
, OXM_ALL
)) != NULL
||
71 (cp
= xok_vlook(ssl_verify
, urlp
, OXM_ALL
)) != NULL
){
73 if(!asccasecmp(a_tls_verify_levels
[i
].tv_name
, cp
)){
74 n_tls_verify_level
= a_tls_verify_levels
[i
].tv_level
;
76 }else if(++i
>= n_NELEM(a_tls_verify_levels
)){
77 n_err(_("Invalid value of *tls-verify*: %s\n"), cp
);
85 n_tls_verify_decide(void){
89 switch(n_tls_verify_level
){
91 case n_TLS_VERIFY_STRICT
:
94 case n_TLS_VERIFY_ASK
:
95 rv
= getapproval(NULL
, FAL0
);
97 case n_TLS_VERIFY_WARN
:
98 case n_TLS_VERIFY_IGNORE
:
107 smime_split(FILE *ip
, FILE **hp
, FILE **bp
, long xcount
, int keep
)
110 struct myline
*ml_next
;
112 char ml_buf
[n_VFIELD_SIZE(0)];
115 size_t bufsize
, buflen
, cnt
;
120 if ((*hp
= Ftmp(NULL
, "smimeh", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
)
122 if ((*bp
= Ftmp(NULL
, "smimeb", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)
126 n_perr(_("tempfile"), 0);
131 buf
= n_alloc(bufsize
= LINESIZE
);
132 cnt
= (xcount
< 0) ? fsize(ip
) : xcount
;
134 while (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, ip
, 0) != NULL
&&
136 if (!ascncasecmp(buf
, "content-", 8)) {
138 fputs("X-Encoded-", *hp
);
140 struct myline
*ml
= n_alloc(n_VSTRUCT_SIZEOF(struct myline
, ml_buf
149 memcpy(ml
->ml_buf
, buf
, buflen
+1);
151 fwrite(buf
, sizeof *buf
, buflen
, *hp
);
156 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, ip
, 0);
160 fwrite(buf
, sizeof *buf
, buflen
, *hp
);
164 while (head
!= NULL
) {
165 fwrite(head
->ml_buf
, sizeof *head
->ml_buf
, head
->ml_len
, *bp
);
167 head
= head
->ml_next
;
171 while (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, ip
, 0) != NULL
)
172 fwrite(buf
, sizeof *buf
, buflen
, *bp
);
183 smime_sign_assemble(FILE *hp
, FILE *bp
, FILE *sp
, char const *message_digest
)
190 if ((op
= Ftmp(NULL
, "smimea", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)
192 n_perr(_("tempfile"), 0);
196 while ((c
= getc(hp
)) != EOF
) {
197 if (c
== '\n' && lastc
== '\n')
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
)
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"
215 "Content-Description: S/MIME digital signature\n\n", op
);
216 while ((c
= getc(sp
)) != EOF
) {
218 while ((c
= getc(sp
)) != EOF
&& c
!= '\n');
224 fprintf(op
, "\n--%s--\n", boundary
);
232 n_perr(_("signed output data"), 0);
244 smime_encrypt_assemble(FILE *hp
, FILE *yp
)
250 if ((op
= Ftmp(NULL
, "smimee", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)
252 n_perr(_("tempfile"), 0);
256 while ((c
= getc(hp
)) != EOF
) {
257 if (c
== '\n' && lastc
== '\n')
263 fputs("Content-Type: application/pkcs7-mime; name=\"smime.p7m\"\n"
264 "Content-Transfer-Encoding: base64\n"
265 "Content-Disposition: attachment; filename=\"smime.p7m\"\n"
266 "Content-Description: S/MIME encrypted message\n\n", op
);
267 while ((c
= getc(yp
)) != EOF
) {
269 while ((c
= getc(yp
)) != EOF
&& c
!= '\n');
280 n_perr(_("encrypted output data"), 0);
292 smime_decrypt_assemble(struct message
*m
, FILE *hp
, FILE *bp
)
297 size_t bufsize
= 0, buflen
, cnt
;
298 long lns
= 0, octets
= 0;
303 x
= n_autorec_alloc(sizeof *x
);
306 fseek(mb
.mb_otf
, 0L, SEEK_END
);
307 offset
= ftell(mb
.mb_otf
);
310 while (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, hp
, 0) != NULL
) {
314 if ((cp
= thisfield(buf
, "content-transfer-encoding")) != NULL
)
315 if (!ascncasecmp(cp
, "binary", 7))
317 fwrite(buf
, sizeof *buf
, buflen
, mb
.mb_otf
);
322 { struct time_current save
= time_current
;
323 time_current_update(&time_current
, TRU1
);
324 octets
+= mkdate(mb
.mb_otf
, "X-Decoding-Date");
330 while (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, bp
, 0) != NULL
) {
332 if (!binary
&& buf
[buflen
- 1] == '\n' && buf
[buflen
- 2] == '\r')
333 buf
[--buflen
- 1] = '\n';
334 fwrite(buf
, sizeof *buf
, buflen
, mb
.mb_otf
);
338 else if (buf
[buflen
- 1] == '\n')
344 while (!binary
&& lastnl
< 2) {
345 putc('\n', mb
.mb_otf
);
356 if (ferror(mb
.mb_otf
)) {
357 n_perr(_("decrypted output data"), 0);
360 x
->m_size
= x
->m_xsize
= octets
;
361 x
->m_lines
= x
->m_xlines
= lns
;
362 x
->m_block
= mailx_blockof(offset
);
363 x
->m_offset
= mailx_offsetof(offset
);
370 c_certsave(void *vp
){
373 struct n_cmd_arg_ctx
*cacp
;
377 assert(cacp
->cac_no
== 2);
379 msgvec
= cacp
->cac_arg
->ca_arg
.ca_msglist
;
383 file
= cacp
->cac_arg
->ca_next
->ca_arg
.ca_str
.s
;
384 if((cp
= fexpand(file
, FEXP_LOCAL_FILE
| FEXP_NOPROTO
)) == NULL
||
386 n_err(_("`certsave': file expansion failed: %s\n"),
387 n_shexp_quote_cp(file
, FAL0
));
393 if((fp
= Fopen(file
, "a")) == NULL
){
400 for(ip
= msgvec
; *ip
!= 0; ++ip
)
401 if(smime_certsave(&message
[*ip
- 1], *ip
, fp
) != OKAY
)
407 fprintf(n_stdout
, "Certificate(s) saved\n");
414 n_tls_rfc2595_hostname_match(char const *host
, char const *pattern
){
418 if(pattern
[0] == '*' && pattern
[1] == '.'){
420 while(*host
&& *host
!= '.')
423 rv
= (asccasecmp(host
, pattern
) == 0);
431 char const **argv
, *varname
, *varres
, *cp
;
435 vp
= NULL
; /* -> return value (boolean) */
436 varname
= (n_pstate
& n_PS_ARGMOD_VPUT
) ? *argv
++ : NULL
;
439 if((cp
= argv
[0])[0] == '\0')
441 else if(is_asccaseprefix(cp
, "fingerprint")){
443 n_err(_("`tls': fingerprint: no +sockets in *features*\n"));
444 n_pstate_err_no
= n_ERR_OPNOTSUPP
;
450 if(argv
[1] == NULL
|| argv
[2] != NULL
)
452 if((i
= strlen(*++argv
)) >= UI32_MAX
)
453 goto jeoverflow
; /* TODO generic for ALL commands!! */
454 if(!url_parse(&url
, CPROTO_CERTINFO
, *argv
))
456 if(!sopen(&so
, &url
)){ /* auto-closes for CPROTO_CERTINFO on success */
457 n_pstate_err_no
= n_err_no
;
460 if(so
.s_tls_finger
== NULL
)
462 varres
= so
.s_tls_finger
;
463 #endif /* HAVE_SOCKETS */
467 n_pstate_err_no
= n_ERR_NONE
;
471 if(fprintf(n_stdout
, "%s\n", varres
) < 0){
472 n_pstate_err_no
= n_err_no
;
475 }else if(!n_var_vset(varname
, (uintptr_t)varres
)){
476 n_pstate_err_no
= n_ERR_NOTSUP
;
483 n_err(_("`tls': string length or offset overflows datatype\n"));
484 n_pstate_err_no
= n_ERR_OVERFLOW
;
488 n_err(_("`tls': invalid subcommand: %s\n"),
489 n_shexp_quote_cp(*argv
, FAL0
));
491 n_err(_("Synopsis: tls: <command> [<:argument:>]\n"));
493 n_pstate_err_no
= n_ERR_INVAL
;
496 #endif /* HAVE_TLS */