From: Stefan Metzmacher Date: Thu, 3 Apr 2025 15:32:58 +0000 (+0200) Subject: s3:smbd: add support for SMB_TRANSPORT_TYPE_QUIC X-Git-Tag: tdb-1.4.14~116 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=ded5ac15b6707c359b6e84d1e85577b00b1f582d;p=thirdparty%2Fsamba.git s3:smbd: add support for SMB_TRANSPORT_TYPE_QUIC This requires https://github.com/lxin/quic, which provides a kernel module quic.ko for Linux (tested with Linux 6.8 and 6.14). The userspace libquic is mirrored under third_party/quic for now. This can be activated by adding 'quic' to 'server smb transports'. The following smb.conf options are also relevant: 'tls enabled' 'tls cafile' 'tls certfile' 'tls keyfile' If the files pointed to by 'tls cafile', 'tls certfile' and 'tls keyfile' all don't exist, self-signed tls certificates are generated automatically at startup. Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme --- diff --git a/docs-xml/smbdotconf/protocol/serversmbtransports.xml b/docs-xml/smbdotconf/protocol/serversmbtransports.xml index 83a4c62ddc0..f506e4e862a 100644 --- a/docs-xml/smbdotconf/protocol/serversmbtransports.xml +++ b/docs-xml/smbdotconf/protocol/serversmbtransports.xml @@ -19,6 +19,27 @@ after ':', e.g. 'nbt:1139'. + The transport 'quic' uses the quic protocol on top of udp. + The default port for 'quic' is 443. Other ports can be specified by adding it + after ':', e.g. 'quic:1443'. + The following options are also relevant: + , + , + and + . + If the files pointed to by + , + and + all do not exist, + a self-signed tls certificate is generated automatically at startup. + + + + Note: 'quic' requires the quic.ko kernel module for Linux from + https://github.com/lxin/quic (tested with Linux 6.14). Future + Linux versions may support it natively. + + Numerical ports are handled as 'tcp' except port '139' is handled as 'nbt'. @@ -29,6 +50,8 @@ 445 tcp, tcp:1445 8000, nbt:1139 +tcp, quic, nbt ++quic tcp, nbt diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 97d0fbbbc60..f7f55420c00 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -60,6 +60,11 @@ #include "lib/global_contexts.h" #include "source3/lib/substitute.h" #include "lib/addrchange.h" +#include "../source4/lib/tls/tls.h" + +#ifdef HAVE_LIBQUIC +#include +#endif #ifdef CLUSTER_SUPPORT #include "ctdb_protocol.h" @@ -87,6 +92,8 @@ struct smbd_parent_context { struct server_id notifyd; struct tevent_timer *cleanup_te; + + struct tstream_tls_params *quic_tlsp; }; struct smbd_open_socket { @@ -244,6 +251,76 @@ static void smb_parent_send_to_children(struct messaging_context *ctx, messaging_send_to_children(ctx, msg_type, msg_data); } +static NTSTATUS smb_parent_load_tls_certificates(struct smbd_parent_context *parent, + struct loadparm_context *lp_ctx) +{ + struct tstream_tls_params *quic_tlsp = NULL; + const char *dns_hostname = NULL; + NTSTATUS status; + + if (parent == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + dns_hostname = lpcfg_dns_hostname(lp_ctx); + if (dns_hostname == NULL) { + DBG_ERR("ERROR: lpcfg_dns_hostname() failed\n"); + return NT_STATUS_INTERNAL_ERROR; + } + + status = tstream_tls_params_server_lpcfg(parent, + dns_hostname, + lp_ctx, + &quic_tlsp); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("tstream_tls_params_server_lpcfg(): %s\n", + nt_errstr(status)); + return status; + } + + status = tstream_tls_params_quic_prepare(quic_tlsp); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("tstream_tls_params_quic_prepare(): %s\n", + nt_errstr(status)); + return status; + } + + TALLOC_FREE(parent->quic_tlsp); + parent->quic_tlsp = quic_tlsp; + return NT_STATUS_OK; +} + +static void smb_parent_reload_tls_certificates(struct messaging_context *ctx, + void *private_data, + uint32_t msg_type, + struct server_id srv_id, + DATA_BLOB* msg_data) +{ + struct smbd_parent_context *parent = am_parent; + struct loadparm_context *lp_ctx = NULL; + NTSTATUS status; + + if (parent == NULL) { + return; + } + + lp_ctx = loadparm_init_s3(talloc_tos(), loadparm_s3_helpers()); + if (lp_ctx == NULL) { + DBG_ERR("loadparm_init_s3() failed\n"); + return; + } + + status = smb_parent_load_tls_certificates(parent, lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("smb_parent_load_tls_certificates(): %s\n", + nt_errstr(status)); + return; + } + + DBG_DEBUG("smb_parent_load_tls_certificates(): %s\n", + nt_errstr(status)); +} + /* * Parent smbd process sets its own debug level first and then * sends a message to all the smbd children to adjust their debug @@ -988,6 +1065,26 @@ static void smbd_accept_connection(struct tevent_context *ev, exit_server("reinit_after_fork() failed"); return; } + if (transport_type == SMB_TRANSPORT_TYPE_QUIC) { + struct tstream_tls_params *quic_tlsp = + s->parent->quic_tlsp; + + /* + * In interactive mode it's ok to do a + * sync handshake, there's no point in + * doing it async. + */ + status = tstream_tls_quic_handshake(quic_tlsp, + true, /* is_server */ + 5000, /* 5 secs */ + "smb", + fd); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("tstream_tls_quic_handshake(%d): %s\n", + fd, nt_errstr(status)); + exit_server_cleanly("tstream_tls_quic_handshake"); + } + } smbd_process(ev, msg_ctx, fd, true, transport_type); exit_server_cleanly("end of interactive mode"); return; @@ -1001,9 +1098,15 @@ static void smbd_accept_connection(struct tevent_context *ev, pid = fork(); if (pid == 0) { enum smb_transport_type transport_type = s->transport.type; + struct tstream_tls_params *quic_tlsp = NULL; char addrstr[INET6_ADDRSTRLEN]; NTSTATUS status = NT_STATUS_OK; + if (transport_type == SMB_TRANSPORT_TYPE_QUIC) { + quic_tlsp = talloc_move(talloc_tos(), + &s->parent->quic_tlsp); + } + /* * Can't use TALLOC_FREE here. Nulling out the argument to it * would overwrite memory we've just freed. @@ -1042,6 +1145,25 @@ static void smbd_accept_connection(struct tevent_context *ev, print_sockaddr(addrstr, sizeof(addrstr), &caddr.u.ss); process_set_title("smbd[%s]", "client [%s]", addrstr); + if (transport_type == SMB_TRANSPORT_TYPE_QUIC) { + /* + * We just forked and this process only + * handles a single connection, so it's ok + * to do a sync handshake, there's no point in + * doing it async. + */ + status = tstream_tls_quic_handshake(quic_tlsp, + true, /* is_server */ + 5000, /* 5 secs */ + "smb", + fd); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("tstream_tls_quic_handshake(%d): %s\n", + fd, nt_errstr(status)); + exit_server_cleanly("tstream_tls_quic_handshake"); + } + } + TALLOC_FREE(quic_tlsp); smbd_process(ev, msg_ctx, fd, false, transport_type); exit: exit_server_cleanly("end of child"); @@ -1103,9 +1225,11 @@ static bool smbd_open_one_socket(struct smbd_parent_context *parent, rebind = true; break; case SMB_TRANSPORT_TYPE_QUIC: - /* - * Not supported yet - */ +#ifdef HAVE_LIBQUIC + port = transport->port; + protocol = IPPROTO_QUIC; + rebind = false; +#endif break; case SMB_TRANSPORT_TYPE_UNKNOWN: /* @@ -1122,7 +1246,7 @@ static bool smbd_open_one_socket(struct smbd_parent_context *parent, return false; } - s = talloc(parent, struct smbd_open_socket); + s = talloc_zero(parent, struct smbd_open_socket); if (!s) { return false; } @@ -1142,8 +1266,14 @@ static bool smbd_open_one_socket(struct smbd_parent_context *parent, } /* ready to listen */ - set_socket_options(s->fd, "SO_KEEPALIVE"); - set_socket_options(s->fd, lp_socket_options()); + if (transport->type == SMB_TRANSPORT_TYPE_QUIC) { +#ifdef HAVE_LIBQUIC + setsockopt(s->fd, SOL_QUIC, QUIC_SOCKOPT_ALPN, "smb", strlen("smb")); +#endif /* HAVE_LIBQUIC */ + } else { + set_socket_options(s->fd, "SO_KEEPALIVE"); + set_socket_options(s->fd, lp_socket_options()); + } /* Set server socket to * non-blocking for the accept. */ @@ -1328,6 +1458,13 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent, messaging_register(msg_ctx, NULL, MSG_SMB_NOTIFY_STARTED, smb_parent_send_to_children); + if (parent->quic_tlsp != NULL) { + messaging_register(msg_ctx, + NULL, + MSG_RELOAD_TLS_CERTIFICATES, + smb_parent_reload_tls_certificates); + } + if (lp_interfaces() && lp_bind_interfaces_only()) { messaging_register(msg_ctx, NULL, @@ -1858,6 +1995,8 @@ extern void build_options(bool screen); .exit_server = smbd_exit_server, .exit_server_cleanly = smbd_exit_server_cleanly, }; + uint8_t ti; + bool quic_requested = false; bool ok; setproctitle_init(argc, discard_const(argv), environ); @@ -2165,6 +2304,57 @@ extern void build_options(bool screen); } } + for (ti = 0; ti < parent->transports.num_transports; ti++) { + const struct smb_transport *t = + &parent->transports.transports[ti]; + + if (t->type == SMB_TRANSPORT_TYPE_QUIC) { + quic_requested = true; + break; + } + } + + if (quic_requested) { + status = smb_parent_load_tls_certificates(parent, lp_ctx); + if (NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) { + ok = false; + goto quic_disabled; + } + if (!NT_STATUS_IS_OK(status)) { + exit_server("ERROR: smb_parent_load_tls_certificates"); + } + + ok = tstream_tls_params_quic_enabled(parent->quic_tlsp); +quic_disabled: + if (!ok) { + struct smb_transports tt = parent->transports; + struct smb_transports *ts = &parent->transports; + + DBG_ERR("WARNING: ignore listening on transport 'quic'\n"); + + /* + * Filter out SMB_TRANSPORT_TYPE_QUIC + */ + + ts->num_transports = 0; + for (ti = 0; ti < tt.num_transports; ti++) { + const struct smb_transport *t = + &tt.transports[ti]; + + if (t->type == SMB_TRANSPORT_TYPE_QUIC) { + continue; + } + + ts->transports[ts->num_transports] = *t; + ts->num_transports += 1; + } + } + } + + if (parent->transports.num_transports == 0) { + exit_server("No transports configured for listening"); + } + se = tevent_add_signal(parent->ev_ctx, parent, SIGTERM, 0, diff --git a/source3/smbd/smb2_process.c b/source3/smbd/smb2_process.c index 590c7f360d2..153f9a430b6 100644 --- a/source3/smbd/smb2_process.c +++ b/source3/smbd/smb2_process.c @@ -2100,6 +2100,8 @@ void smbd_process(struct tevent_context *ev_ctx, messaging_deregister(sconn->msg_ctx, MSG_SMB_TELL_NUM_CHILDREN, NULL); + messaging_deregister(sconn->msg_ctx, MSG_RELOAD_TLS_CERTIFICATES, NULL); + /* * Use the default MSG_DEBUG handler to avoid rebroadcasting * MSGs to all child processes diff --git a/source3/wscript_build b/source3/wscript_build index 2870f1a704b..472668a595b 100644 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -764,6 +764,7 @@ bld.SAMBA3_LIBRARY('smbd_base', fd_handle cli_spoolss samba3-namearray + LIBTLS ''' + bld.env['dmapi_lib'] + bld.env['legacy_quota_libs'] +