1 /* $NetBSD: tls_mgr.c,v 1.4 2022/10/08 16:12:50 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* tls_mgr 3
6 /* SUMMARY
7 /* tlsmgr client interface
8 /* SYNOPSIS
9 /* #include <tls_mgr.h>
10 /*
11 /* int tls_mgr_seed(buf, len)
12 /* VSTRING *buf;
13 /* int len;
14 /*
15 /* int tls_mgr_policy(cache_type, cachable, timeout)
16 /* const char *cache_type;
17 /* int *cachable;
18 /* int *timeout;
19 /*
20 /* int tls_mgr_update(cache_type, cache_id, buf, len)
21 /* const char *cache_type;
22 /* const char *cache_id;
23 /* const char *buf;
24 /* ssize_t len;
25 /*
26 /* int tls_mgr_lookup(cache_type, cache_id, buf)
27 /* const char *cache_type;
28 /* const char *cache_id;
29 /* VSTRING *buf;
30 /*
31 /* int tls_mgr_delete(cache_type, cache_id)
32 /* const char *cache_type;
33 /* const char *cache_id;
34 /*
35 /* TLS_TICKET_KEY *tls_mgr_key(keyname, timeout)
36 /* unsigned char *keyname;
37 /* int timeout;
38 /* DESCRIPTION
39 /* These routines communicate with the tlsmgr(8) server for
40 /* entropy and session cache management. Since these are
41 /* non-critical services, requests are allowed to fail without
42 /* disrupting Postfix.
43 /*
44 /* tls_mgr_seed() requests entropy from the tlsmgr(8)
45 /* Pseudo Random Number Generator (PRNG) pool.
46 /*
47 /* tls_mgr_policy() requests the session caching policy.
48 /*
49 /* tls_mgr_lookup() loads the specified session from
50 /* the specified session cache.
51 /*
52 /* tls_mgr_update() saves the specified session to
53 /* the specified session cache.
54 /*
55 /* tls_mgr_delete() removes specified session from
56 /* the specified session cache.
57 /*
58 /* tls_mgr_key() is used to retrieve the current TLS session ticket
59 /* encryption or decryption keys.
60 /*
61 /* Arguments:
62 /* .IP cache_type
63 /* One of TLS_MGR_SCACHE_SMTPD, TLS_MGR_SCACHE_SMTP or
64 /* TLS_MGR_SCACHE_LMTP.
65 /* .IP cachable
66 /* Pointer to int, set non-zero if the requested cache_type
67 /* is enabled.
68 /* .IP timeout
69 /* Pointer to int, returns the cache entry timeout.
70 /* .IP cache_id
71 /* The session cache lookup key.
72 /* .IP buf
73 /* The result or input buffer.
74 /* .IP len
75 /* The length of the input buffer, or the amount of data requested.
76 /* .IP keyname
77 /* Is null when requesting the current encryption keys. Otherwise,
78 /* keyname is a pointer to an array of TLS_TICKET_NAMELEN unsigned
79 /* chars (not NUL terminated) that is an identifier for a key
80 /* previously used to encrypt a session ticket. When encrypting
81 /* a null result indicates that session tickets are not supported, when
82 /* decrypting it indicates that no matching keys were found.
83 /* .IP timeout
84 /* The encryption key timeout. Once a key has been active for this many
85 /* seconds it is retired and used only for decrypting previously issued
86 /* session tickets for another timeout seconds, and is then destroyed.
87 /* The timeout must not be longer than half the SSL session lifetime.
88 /* DIAGNOSTICS
89 /* All client functions return one of the following status codes:
90 /* .IP TLS_MGR_STAT_OK
91 /* The request completed, and the requested operation was
92 /* successful (for example, the requested session was found,
93 /* or the specified session was saved or removed).
94 /* .IP TLS_MGR_STAT_ERR
95 /* The request completed, but the requested operation failed
96 /* (for example, the requested object was not found or the
97 /* specified session was not saved or removed).
98 /* .IP TLS_MGR_STAT_FAIL
99 /* The request could not complete (the client could not
100 /* communicate with the tlsmgr(8) server).
101 /* SEE ALSO
102 /* tlsmgr(8) TLS session and PRNG management
103 /* LICENSE
104 /* .ad
105 /* .fi
106 /* The Secure Mailer license must be distributed with this software.
107 /* AUTHOR(S)
108 /* Wietse Venema
109 /* IBM T.J. Watson Research
110 /* P.O. Box 704
111 /* Yorktown Heights, NY 10598, USA
112 /*
113 /* Wietse Venema
114 /* Google, Inc.
115 /* 111 8th Avenue
116 /* New York, NY 10011, USA
117 /*--*/
118
119 /* System library. */
120
121 #include <sys_defs.h>
122
123 #ifdef USE_TLS
124
125 #ifdef STRCASECMP_IN_STRINGS_H
126 #include <strings.h>
127 #endif
128
129 /* Utility library. */
130
131 #include <msg.h>
132 #include <vstream.h>
133 #include <vstring.h>
134 #include <attr.h>
135 #include <attr_clnt.h>
136 #include <mymalloc.h>
137 #include <stringops.h>
138
139 /* Global library. */
140
141 #include <mail_params.h>
142 #include <mail_proto.h>
143
144 /* TLS library. */
145 #include <tls_mgr.h>
146
147 /* Application-specific. */
148
149 #define STR(x) vstring_str(x)
150 #define LEN(x) VSTRING_LEN(x)
151
152 static ATTR_CLNT *tls_mgr;
153
154 /* tls_mgr_handshake - receive server protocol announcement */
155
tls_mgr_handshake(VSTREAM * stream)156 static int tls_mgr_handshake(VSTREAM *stream)
157 {
158 return (attr_scan(stream, ATTR_FLAG_STRICT,
159 RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TLSMGR),
160 ATTR_TYPE_END));
161 }
162
163 /* tls_mgr_open - create client handle */
164
tls_mgr_open(void)165 static void tls_mgr_open(void)
166 {
167 char *service;
168
169 /*
170 * Sanity check.
171 */
172 if (tls_mgr != 0)
173 msg_panic("tls_mgr_open: multiple initialization");
174
175 /*
176 * Use whatever IPC is preferred for internal use: UNIX-domain sockets or
177 * Solaris streams.
178 */
179 service = concatenate("local:" TLS_MGR_CLASS "/", var_tls_mgr_service,
180 (char *) 0);
181 tls_mgr = attr_clnt_create(service, var_ipc_timeout,
182 var_ipc_idle_limit, var_ipc_ttl_limit);
183 myfree(service);
184
185 attr_clnt_control(tls_mgr,
186 ATTR_CLNT_CTL_PROTO, attr_vprint, attr_vscan,
187 ATTR_CLNT_CTL_HANDSHAKE, tls_mgr_handshake,
188 ATTR_CLNT_CTL_END);
189 }
190
191 /* tls_mgr_seed - request PRNG seed */
192
tls_mgr_seed(VSTRING * buf,int len)193 int tls_mgr_seed(VSTRING *buf, int len)
194 {
195 int status;
196
197 /*
198 * Create the tlsmgr client handle.
199 */
200 if (tls_mgr == 0)
201 tls_mgr_open();
202
203 /*
204 * Request seed.
205 */
206 if (attr_clnt_request(tls_mgr,
207 ATTR_FLAG_NONE, /* Request attributes */
208 SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_SEED),
209 SEND_ATTR_INT(TLS_MGR_ATTR_SIZE, len),
210 ATTR_TYPE_END,
211 ATTR_FLAG_MISSING, /* Reply attributes */
212 RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
213 RECV_ATTR_DATA(TLS_MGR_ATTR_SEED, buf),
214 ATTR_TYPE_END) != 2)
215 status = TLS_MGR_STAT_FAIL;
216 return (status);
217 }
218
219 /* tls_mgr_policy - request caching policy */
220
tls_mgr_policy(const char * cache_type,int * cachable,int * timeout)221 int tls_mgr_policy(const char *cache_type, int *cachable, int *timeout)
222 {
223 int status;
224
225 /*
226 * Create the tlsmgr client handle.
227 */
228 if (tls_mgr == 0)
229 tls_mgr_open();
230
231 /*
232 * Request policy.
233 */
234 if (attr_clnt_request(tls_mgr,
235 ATTR_FLAG_NONE, /* Request attributes */
236 SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_POLICY),
237 SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type),
238 ATTR_TYPE_END,
239 ATTR_FLAG_MISSING, /* Reply attributes */
240 RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
241 RECV_ATTR_INT(TLS_MGR_ATTR_CACHABLE, cachable),
242 RECV_ATTR_INT(TLS_MGR_ATTR_SESSTOUT, timeout),
243 ATTR_TYPE_END) != 3)
244 status = TLS_MGR_STAT_FAIL;
245 return (status);
246 }
247
248 /* tls_mgr_lookup - request cached session */
249
tls_mgr_lookup(const char * cache_type,const char * cache_id,VSTRING * buf)250 int tls_mgr_lookup(const char *cache_type, const char *cache_id,
251 VSTRING *buf)
252 {
253 int status;
254
255 /*
256 * Create the tlsmgr client handle.
257 */
258 if (tls_mgr == 0)
259 tls_mgr_open();
260
261 /*
262 * Send the request and receive the reply.
263 */
264 if (attr_clnt_request(tls_mgr,
265 ATTR_FLAG_NONE, /* Request */
266 SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_LOOKUP),
267 SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type),
268 SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id),
269 ATTR_TYPE_END,
270 ATTR_FLAG_MISSING, /* Reply */
271 RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
272 RECV_ATTR_DATA(TLS_MGR_ATTR_SESSION, buf),
273 ATTR_TYPE_END) != 2)
274 status = TLS_MGR_STAT_FAIL;
275 return (status);
276 }
277
278 /* tls_mgr_update - save session to cache */
279
tls_mgr_update(const char * cache_type,const char * cache_id,const char * buf,ssize_t len)280 int tls_mgr_update(const char *cache_type, const char *cache_id,
281 const char *buf, ssize_t len)
282 {
283 int status;
284
285 /*
286 * Create the tlsmgr client handle.
287 */
288 if (tls_mgr == 0)
289 tls_mgr_open();
290
291 /*
292 * Send the request and receive the reply.
293 */
294 if (attr_clnt_request(tls_mgr,
295 ATTR_FLAG_NONE, /* Request */
296 SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_UPDATE),
297 SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type),
298 SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id),
299 SEND_ATTR_DATA(TLS_MGR_ATTR_SESSION, len, buf),
300 ATTR_TYPE_END,
301 ATTR_FLAG_MISSING, /* Reply */
302 RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
303 ATTR_TYPE_END) != 1)
304 status = TLS_MGR_STAT_FAIL;
305 return (status);
306 }
307
308 /* tls_mgr_delete - remove cached session */
309
tls_mgr_delete(const char * cache_type,const char * cache_id)310 int tls_mgr_delete(const char *cache_type, const char *cache_id)
311 {
312 int status;
313
314 /*
315 * Create the tlsmgr client handle.
316 */
317 if (tls_mgr == 0)
318 tls_mgr_open();
319
320 /*
321 * Send the request and receive the reply.
322 */
323 if (attr_clnt_request(tls_mgr,
324 ATTR_FLAG_NONE, /* Request */
325 SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_DELETE),
326 SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type),
327 SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id),
328 ATTR_TYPE_END,
329 ATTR_FLAG_MISSING, /* Reply */
330 RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
331 ATTR_TYPE_END) != 1)
332 status = TLS_MGR_STAT_FAIL;
333 return (status);
334 }
335
336 /* request_scache_key - ask tlsmgr(8) for matching key */
337
request_scache_key(unsigned char * keyname)338 static TLS_TICKET_KEY *request_scache_key(unsigned char *keyname)
339 {
340 TLS_TICKET_KEY tmp;
341 static VSTRING *keybuf;
342 char *name;
343 size_t len;
344 int status;
345
346 /*
347 * Create the tlsmgr client handle.
348 */
349 if (tls_mgr == 0)
350 tls_mgr_open();
351
352 if (keybuf == 0)
353 keybuf = vstring_alloc(sizeof(tmp));
354
355 /* In tlsmgr requests we encode null key names as empty strings. */
356 name = keyname ? (char *) keyname : "";
357 len = keyname ? TLS_TICKET_NAMELEN : 0;
358
359 /*
360 * Send the request and receive the reply.
361 */
362 if (attr_clnt_request(tls_mgr,
363 ATTR_FLAG_NONE, /* Request */
364 SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_TKTKEY),
365 SEND_ATTR_DATA(TLS_MGR_ATTR_KEYNAME, len, name),
366 ATTR_TYPE_END,
367 ATTR_FLAG_MISSING, /* Reply */
368 RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
369 RECV_ATTR_DATA(TLS_MGR_ATTR_KEYBUF, keybuf),
370 ATTR_TYPE_END) != 2
371 || status != TLS_MGR_STAT_OK
372 || LEN(keybuf) != sizeof(tmp))
373 return (0);
374
375 memcpy((void *) &tmp, STR(keybuf), sizeof(tmp));
376 return (tls_scache_key_rotate(&tmp));
377 }
378
379 /* tls_mgr_key - session ticket key lookup, local cache, then tlsmgr(8) */
380
tls_mgr_key(unsigned char * keyname,int timeout)381 TLS_TICKET_KEY *tls_mgr_key(unsigned char *keyname, int timeout)
382 {
383 TLS_TICKET_KEY *key = 0;
384 time_t now = time((time_t *) 0);
385
386 /* A zero timeout disables session tickets. */
387 if (timeout <= 0)
388 return (0);
389
390 if ((key = tls_scache_key(keyname, now, timeout)) == 0)
391 key = request_scache_key(keyname);
392 return (key);
393 }
394
395 #ifdef TEST
396
397 /* System library. */
398
399 #include <stdlib.h>
400
401 /* Utility library. */
402
403 #include <argv.h>
404 #include <msg_vstream.h>
405 #include <vstring_vstream.h>
406 #include <hex_code.h>
407
408 /* Global library. */
409
410 #include <config.h>
411
412 /* Application-specific. */
413
main(int unused_ac,char ** av)414 int main(int unused_ac, char **av)
415 {
416 VSTRING *inbuf = vstring_alloc(10);
417 int status;
418 ARGV *argv = 0;
419
420 msg_vstream_init(av[0], VSTREAM_ERR);
421
422 msg_verbose = 3;
423
424 mail_conf_read();
425 msg_info("using config files in %s", var_config_dir);
426
427 if (chdir(var_queue_dir) < 0)
428 msg_fatal("chdir %s: %m", var_queue_dir);
429
430 while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) {
431 argv = argv_split(STR(inbuf), CHARS_SPACE);
432 if (argv->argc == 0) {
433 argv_free(argv);
434 continue;
435 }
436 #define COMMAND(argv, str, len) \
437 (strcasecmp(argv->argv[0], str) == 0 && argv->argc == len)
438
439 if (COMMAND(argv, "policy", 2)) {
440 int cachable;
441 int timeout;
442
443 status = tls_mgr_policy(argv->argv[1], &cachable, &timeout);
444 vstream_printf("status=%d cachable=%d timeout=%d\n",
445 status, cachable, timeout);
446 } else if (COMMAND(argv, "seed", 2)) {
447 VSTRING *buf = vstring_alloc(10);
448 VSTRING *hex = vstring_alloc(10);
449 int len = atoi(argv->argv[1]);
450
451 status = tls_mgr_seed(buf, len);
452 hex_encode(hex, STR(buf), LEN(buf));
453 vstream_printf("status=%d seed=%s\n", status, STR(hex));
454 vstring_free(hex);
455 vstring_free(buf);
456 } else if (COMMAND(argv, "lookup", 3)) {
457 VSTRING *buf = vstring_alloc(10);
458
459 status = tls_mgr_lookup(argv->argv[1], argv->argv[2], buf);
460 vstream_printf("status=%d session=%.*s\n",
461 status, (int) LEN(buf), STR(buf));
462 vstring_free(buf);
463 } else if (COMMAND(argv, "update", 4)) {
464 status = tls_mgr_update(argv->argv[1], argv->argv[2],
465 argv->argv[3], strlen(argv->argv[3]));
466 vstream_printf("status=%d\n", status);
467 } else if (COMMAND(argv, "delete", 3)) {
468 status = tls_mgr_delete(argv->argv[1], argv->argv[2]);
469 vstream_printf("status=%d\n", status);
470 } else {
471 vstream_printf("usage:\n"
472 "seed byte_count\n"
473 "policy smtpd|smtp|lmtp\n"
474 "lookup smtpd|smtp|lmtp cache_id\n"
475 "update smtpd|smtp|lmtp cache_id session\n"
476 "delete smtpd|smtp|lmtp cache_id\n");
477 }
478 vstream_fflush(VSTREAM_OUT);
479 argv_free(argv);
480 }
481
482 vstring_free(inbuf);
483 return (0);
484 }
485
486 #endif /* TEST */
487
488 #endif /* USE_TLS */
489