X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/dc4ab0a186edc8b270c8fa486104fabc567d25e7..6ede5516b1b75fcbdea965a07240c3d4d02e081a:/src/src/tls.c diff --git a/src/src/tls.c b/src/src/tls.c index a1e78d7cd..3de417eca 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -38,7 +38,7 @@ functions from the OpenSSL or GNU TLS libraries. */ static void tls_per_lib_daemon_init(void); static void tls_per_lib_daemon_tick(void); -static void tls_server_creds_init(void); +static unsigned tls_server_creds_init(void); static void tls_server_creds_invalidate(void); static void tls_client_creds_init(transport_instance *, BOOL); static void tls_client_creds_invalidate(transport_instance *); @@ -82,6 +82,8 @@ static struct kevent kev[KEV_SIZE]; static int kev_used = 0; #endif +static unsigned tls_creds_expire = 0; + /************************************************* * Expand string; give error on failure * *************************************************/ @@ -150,32 +152,39 @@ if (inotify_add_watch(tls_watch_fd, CCS s, IN_ONESHOT | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_MOVE_SELF) >= 0) return TRUE; -DEBUG(D_tls) debug_printf("add_watch: %s\n", strerror(errno)); +DEBUG(D_tls) debug_printf("notify_add_watch: %s\n", strerror(errno)); return FALSE; } # endif # ifdef EXIM_HAVE_KEVENT { -uschar * s; -int fd1, fd2, i, cnt = 0; +uschar * s, * t; +int fd1, fd2, i, j, cnt = 0; struct stat sb; +#ifdef OpenBSD +struct kevent k_dummy; +struct timespec ts = {0}; +#endif +errno = 0; if (Ustrcmp(filename, "system,cache") == 0) return TRUE; for (;;) { + if (kev_used > KEV_SIZE-2) { s = US"out of kev space"; goto bad; } if (!(s = Ustrrchr(filename, '/'))) return FALSE; - if ((lstat(filename, &sb)) < 0) return FALSE; - if (kev_used > KEV_SIZE-2) return FALSE; + s = string_copyn(filename, s - filename); /* mem released by tls_set_watch */ /* The dir open will fail if there is a symlink on the path. Fine; it's too much effort to handle all possible cases; just refuse the preload. */ - if ((fd2 = open(s, O_RDONLY | O_NOFOLLOW)) < 0) return FALSE; + if ((fd2 = open(CCS s, O_RDONLY | O_NOFOLLOW)) < 0) { s = US"open dir"; goto bad; } + if ((lstat(CCS filename, &sb)) < 0) { s = US"lstat"; goto bad; } if (!S_ISLNK(sb.st_mode)) { - if ((fd1 = open(filename, O_RDONLY | O_NOFOLLOW)) < 0) return FALSE; + if ((fd1 = open(CCS filename, O_RDONLY | O_NOFOLLOW)) < 0) + { s = US"open file"; goto bad; } DEBUG(D_tls) debug_printf("watch file '%s'\n", filename); EV_SET(&kev[++kev_used], (uintptr_t)fd1, @@ -200,16 +209,31 @@ for (;;) if (!(S_ISLNK(sb.st_mode))) break; - s = store_get(1024, FALSE); - if ((i = readlink(filename, s, 1024)) < 0) return FALSE; - filename = s; - *(s += i) = '\0'; - store_release_above(s+1); + t = store_get(1024, FALSE); + Ustrncpy(t, s, 1022); + j = Ustrlen(s); + t[j++] = '/'; + if ((i = readlink(CCS filename, (void *)(t+j), 1023-j)) < 0) { s = US"readlink"; goto bad; } + filename = t; + *(t += i+j) = '\0'; + store_release_above(t+1); } +#ifdef OpenBSD +if (kevent(tls_watch_fd, &kev[kev_used-cnt], cnt, &k_dummy, 1, &ts) >= 0) + return TRUE; +#else if (kevent(tls_watch_fd, &kev[kev_used-cnt], cnt, NULL, 0, NULL) >= 0) return TRUE; -DEBUG(D_tls) debug_printf("add_watch: %d, %s\n", strerror(errno)); +#endif +s = US"kevent"; + +bad: +DEBUG(D_tls) + if (errno) + debug_printf("%s: %s: %s\n", __FUNCTION__, s, strerror(errno)); + else + debug_printf("%s: %s\n", __FUNCTION__, s); return FALSE; } # endif /*EXIM_HAVE_KEVENT*/ @@ -272,20 +296,6 @@ struct timespec t = {0}; (void) kevent(fd, NULL, 0, &kev, 1, &t); #endif } - -/* Called, after a delay for multiple file ops to get done, from -the daemon when any of the watches added (above) fire. - -Dump the set of watches and arrange to reload cached creds (which -will set up new watches). */ - -static void -tls_watch_triggered(void) -{ -DEBUG(D_tls) debug_printf("watch triggered\n"); - -tls_daemon_creds_reload(); -} #endif /*EXIM_HAVE_INOTIFY*/ @@ -308,7 +318,7 @@ if (tls_watch_fd < 0) return; #ifdef EXIM_HAVE_KEVENT /* Close the files we had open for kevent */ -for (int fd, i = 0; i < kev_used; i++) +for (int i = 0; i < kev_used; i++) { (void) close((int) kev[i].ident); kev[i].ident = (uintptr_t)-1; @@ -324,12 +334,15 @@ tls_watch_fd = -1; static void tls_daemon_creds_reload(void) { +unsigned lifetime; + #ifdef EXIM_HAVE_KEVENT tls_watch_invalidate(); #endif tls_server_creds_invalidate(); -tls_server_creds_init(); +tls_creds_expire = (lifetime = tls_server_creds_init()) + ? time(NULL) + lifetime : 0; tls_client_creds_reload(TRUE); } @@ -353,10 +366,26 @@ tls_daemon_tick(void) { tls_per_lib_daemon_tick(); #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT) -if (tls_watch_trigger_time && time(NULL) >= tls_watch_trigger_time + 5) +if (tls_creds_expire && time(NULL) >= tls_creds_expire) { - tls_watch_trigger_time = 0; - tls_watch_triggered(); + /* The server cert is a selfsign, with limited lifetime. Dump it and + generate a new one. Reload the rest of the creds also as the machinery + is all there. */ + + DEBUG(D_tls) debug_printf("selfsign cert rotate\n"); + tls_creds_expire = 0; + tls_daemon_creds_reload(); + } +else if (tls_watch_trigger_time && time(NULL) >= tls_watch_trigger_time + 5) + { + /* Called, after a delay for multiple file ops to get done, from + the daemon when any of the watches added (above) fire. + Dump the set of watches and arrange to reload cached creds (which + will set up new watches). */ + + DEBUG(D_tls) debug_printf("watch triggered\n"); + tls_watch_trigger_time = tls_creds_expire = 0; + tls_daemon_creds_reload(); } #endif } @@ -431,6 +460,9 @@ Returns: the character int tls_ungetc(int ch) { +if (ssl_xfer_buffer_lwm <= 0) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "buffer underflow in tls_ungetc"); + ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch; return ch; }