Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : NTP packet signing server
5 :
6 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
7 : Copyright (C) Andrew Tridgell 2005
8 : Copyright (C) Stefan Metzmacher 2005
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : #include "includes.h"
25 : #include "samba/service_task.h"
26 : #include "samba/service.h"
27 : #include "samba/service_stream.h"
28 : #include "samba/process_model.h"
29 : #include "lib/stream/packet.h"
30 : #include "lib/tsocket/tsocket.h"
31 : #include "libcli/util/tstream.h"
32 : #include "librpc/gen_ndr/ndr_ntp_signd.h"
33 : #include "param/param.h"
34 : #include "dsdb/samdb/samdb.h"
35 : #include "auth/auth.h"
36 : #include "libcli/security/security.h"
37 : #include "libcli/ldap/ldap_ndr.h"
38 : #include <ldb.h>
39 : #include <ldb_errors.h>
40 : #include "system/network.h"
41 : #include "system/passwd.h"
42 :
43 : #include "lib/crypto/gnutls_helpers.h"
44 : #include <gnutls/gnutls.h>
45 : #include <gnutls/crypto.h>
46 :
47 : NTSTATUS server_service_ntp_signd_init(TALLOC_CTX *);
48 :
49 : /*
50 : top level context structure for the ntp_signd server
51 : */
52 : struct ntp_signd_server {
53 : struct task_server *task;
54 : struct ldb_context *samdb;
55 : };
56 :
57 : /*
58 : state of an open connection
59 : */
60 : struct ntp_signd_connection {
61 : /* stream connection we belong to */
62 : struct stream_connection *conn;
63 :
64 : /* the ntp_signd_server the connection belongs to */
65 : struct ntp_signd_server *ntp_signd;
66 :
67 : struct tstream_context *tstream;
68 :
69 : struct tevent_queue *send_queue;
70 : };
71 :
72 1 : static void ntp_signd_terminate_connection(struct ntp_signd_connection *ntp_signd_conn, const char *reason)
73 : {
74 1 : stream_terminate_connection(ntp_signd_conn->conn, reason);
75 1 : }
76 :
77 0 : static NTSTATUS signing_failure(struct ntp_signd_connection *ntp_signdconn,
78 : TALLOC_CTX *mem_ctx,
79 : DATA_BLOB *output,
80 : uint32_t packet_id)
81 : {
82 0 : struct signed_reply signed_reply;
83 0 : enum ndr_err_code ndr_err;
84 :
85 0 : signed_reply.op = SIGNING_FAILURE;
86 0 : signed_reply.packet_id = packet_id;
87 0 : signed_reply.signed_packet = data_blob(NULL, 0);
88 :
89 0 : ndr_err = ndr_push_struct_blob(output, mem_ctx, &signed_reply,
90 : (ndr_push_flags_fn_t)ndr_push_signed_reply);
91 :
92 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
93 0 : DEBUG(1,("failed to push ntp error reply\n"));
94 0 : return ndr_map_error2ntstatus(ndr_err);
95 : }
96 :
97 0 : return NT_STATUS_OK;
98 : }
99 :
100 : /*
101 : receive a full packet on a NTP_SIGND connection
102 : */
103 1 : static NTSTATUS ntp_signd_process(struct ntp_signd_connection *ntp_signd_conn,
104 : TALLOC_CTX *mem_ctx,
105 : DATA_BLOB *input,
106 : DATA_BLOB *output)
107 : {
108 0 : const struct dom_sid *domain_sid;
109 0 : struct dom_sid *sid;
110 0 : struct sign_request sign_request;
111 0 : struct signed_reply signed_reply;
112 0 : enum ndr_err_code ndr_err;
113 0 : struct ldb_result *res;
114 1 : const char *attrs[] = { "unicodePwd", "userAccountControl", "cn", NULL };
115 1 : gnutls_hash_hd_t hash_hnd = NULL;
116 0 : struct samr_Password *nt_hash;
117 0 : uint32_t user_account_control;
118 0 : struct dom_sid_buf buf;
119 0 : int ret;
120 :
121 1 : ndr_err = ndr_pull_struct_blob_all(input, mem_ctx,
122 : &sign_request,
123 : (ndr_pull_flags_fn_t)ndr_pull_sign_request);
124 :
125 1 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
126 0 : DEBUG(1,("failed to parse ntp signing request\n"));
127 0 : dump_data(1, input->data, input->length);
128 0 : return ndr_map_error2ntstatus(ndr_err);
129 : }
130 :
131 : /* We need to implement 'check signature' and 'request server
132 : * to sign' operations at some point */
133 1 : if (sign_request.op != SIGN_TO_CLIENT) {
134 0 : return signing_failure(ntp_signd_conn,
135 : mem_ctx,
136 : output,
137 0 : sign_request.packet_id);
138 : }
139 :
140 : /* We need to implement 'check signature' and 'request server
141 : * to sign' operations at some point */
142 1 : if (sign_request.version != NTP_SIGND_PROTOCOL_VERSION_0) {
143 0 : return signing_failure(ntp_signd_conn,
144 : mem_ctx,
145 : output,
146 0 : sign_request.packet_id);
147 : }
148 :
149 1 : domain_sid = samdb_domain_sid(ntp_signd_conn->ntp_signd->samdb);
150 1 : if (domain_sid == NULL) {
151 0 : return signing_failure(ntp_signd_conn,
152 : mem_ctx,
153 : output,
154 0 : sign_request.packet_id);
155 : }
156 :
157 : /* The top bit is a 'key selector' */
158 1 : sid = dom_sid_add_rid(mem_ctx, domain_sid,
159 1 : sign_request.key_id & 0x7FFFFFFF);
160 1 : if (sid == NULL) {
161 0 : talloc_free(mem_ctx);
162 0 : return signing_failure(ntp_signd_conn,
163 : mem_ctx,
164 : output,
165 0 : sign_request.packet_id);
166 : }
167 :
168 2 : ret = ldb_search(ntp_signd_conn->ntp_signd->samdb, mem_ctx,
169 : &res,
170 1 : ldb_get_default_basedn(ntp_signd_conn->ntp_signd->samdb),
171 : LDB_SCOPE_SUBTREE,
172 : attrs,
173 : "(&(objectSid=%s)(objectClass=user))",
174 : ldap_encode_ndr_dom_sid(mem_ctx, sid));
175 1 : if (ret != LDB_SUCCESS) {
176 0 : DEBUG(2, ("Failed to search for SID %s in SAM for NTP signing: "
177 : "%s\n",
178 : dom_sid_str_buf(sid, &buf),
179 : ldb_errstring(ntp_signd_conn->ntp_signd->samdb)));
180 0 : return signing_failure(ntp_signd_conn,
181 : mem_ctx,
182 : output,
183 0 : sign_request.packet_id);
184 : }
185 :
186 1 : if (res->count == 0) {
187 0 : DEBUG(2, ("Failed to find SID %s in SAM for NTP signing\n",
188 : dom_sid_str_buf(sid, &buf)));
189 0 : return signing_failure(ntp_signd_conn,
190 : mem_ctx,
191 : output,
192 0 : sign_request.packet_id);
193 1 : } else if (res->count != 1) {
194 0 : DEBUG(1, ("Found SID %s %u times in SAM for NTP signing\n",
195 : dom_sid_str_buf(sid, &buf),
196 : res->count));
197 0 : return signing_failure(ntp_signd_conn,
198 : mem_ctx,
199 : output,
200 0 : sign_request.packet_id);
201 : }
202 :
203 1 : user_account_control = ldb_msg_find_attr_as_uint(res->msgs[0],
204 : "userAccountControl",
205 : 0);
206 :
207 1 : if (user_account_control & UF_ACCOUNTDISABLE) {
208 0 : DEBUG(1, ("Account %s for SID [%s] is disabled\n",
209 : ldb_dn_get_linearized(res->msgs[0]->dn),
210 : dom_sid_str_buf(sid, &buf)));
211 0 : return NT_STATUS_ACCESS_DENIED;
212 : }
213 :
214 1 : if (!(user_account_control & (UF_INTERDOMAIN_TRUST_ACCOUNT|UF_SERVER_TRUST_ACCOUNT|UF_WORKSTATION_TRUST_ACCOUNT))) {
215 0 : DEBUG(1, ("Account %s for SID [%s] is not a trust account\n",
216 : ldb_dn_get_linearized(res->msgs[0]->dn),
217 : dom_sid_str_buf(sid, &buf)));
218 0 : return NT_STATUS_ACCESS_DENIED;
219 : }
220 :
221 1 : nt_hash = samdb_result_hash(mem_ctx, res->msgs[0], "unicodePwd");
222 1 : if (!nt_hash) {
223 0 : DEBUG(1, ("No unicodePwd found on record of SID %s "
224 : "for NTP signing\n",
225 : dom_sid_str_buf(sid, &buf)));
226 0 : return signing_failure(ntp_signd_conn,
227 : mem_ctx,
228 : output,
229 0 : sign_request.packet_id);
230 : }
231 :
232 : /* Generate the reply packet */
233 1 : signed_reply.packet_id = sign_request.packet_id;
234 1 : signed_reply.op = SIGNING_SUCCESS;
235 1 : signed_reply.signed_packet = data_blob_talloc(mem_ctx,
236 : NULL,
237 : sign_request.packet_to_sign.length + 20);
238 :
239 1 : if (!signed_reply.signed_packet.data) {
240 0 : return signing_failure(ntp_signd_conn,
241 : mem_ctx,
242 : output,
243 0 : sign_request.packet_id);
244 : }
245 :
246 1 : memcpy(signed_reply.signed_packet.data, sign_request.packet_to_sign.data, sign_request.packet_to_sign.length);
247 1 : SIVAL(signed_reply.signed_packet.data, sign_request.packet_to_sign.length, sign_request.key_id);
248 :
249 : /* Sign the NTP response with the unicodePwd */
250 1 : ret = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
251 1 : if (ret < 0) {
252 0 : return gnutls_error_to_ntstatus(ret, NT_STATUS_HASH_NOT_SUPPORTED);
253 : }
254 :
255 1 : ret = gnutls_hash(hash_hnd,
256 1 : nt_hash->hash,
257 : sizeof(nt_hash->hash));
258 1 : if (ret < 0) {
259 0 : gnutls_hash_deinit(hash_hnd, NULL);
260 0 : return gnutls_error_to_ntstatus(ret, NT_STATUS_HASH_NOT_SUPPORTED);
261 : }
262 1 : ret = gnutls_hash(hash_hnd,
263 1 : sign_request.packet_to_sign.data,
264 : sign_request.packet_to_sign.length);
265 1 : if (ret < 0) {
266 0 : gnutls_hash_deinit(hash_hnd, NULL);
267 0 : return gnutls_error_to_ntstatus(ret, NT_STATUS_HASH_NOT_SUPPORTED);
268 : }
269 :
270 1 : gnutls_hash_deinit(hash_hnd,
271 1 : signed_reply.signed_packet.data +
272 1 : sign_request.packet_to_sign.length + 4);
273 :
274 : /* Place it into the packet for the wire */
275 1 : ndr_err = ndr_push_struct_blob(output, mem_ctx, &signed_reply,
276 : (ndr_push_flags_fn_t)ndr_push_signed_reply);
277 :
278 1 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
279 0 : DEBUG(1,("failed to push ntp error reply\n"));
280 0 : return ndr_map_error2ntstatus(ndr_err);
281 : }
282 :
283 1 : return NT_STATUS_OK;
284 : }
285 :
286 : /*
287 : called on a tcp recv
288 : */
289 0 : static void ntp_signd_recv(struct stream_connection *conn, uint16_t flags)
290 : {
291 0 : struct ntp_signd_connection *ntp_signd_conn = talloc_get_type(conn->private_data,
292 : struct ntp_signd_connection);
293 0 : ntp_signd_terminate_connection(ntp_signd_conn,
294 : "ntp_signd_recv: called");
295 0 : }
296 :
297 : /*
298 : called when we can write to a connection
299 : */
300 0 : static void ntp_signd_send(struct stream_connection *conn, uint16_t flags)
301 : {
302 0 : struct ntp_signd_connection *ntp_signd_conn = talloc_get_type(conn->private_data,
303 : struct ntp_signd_connection);
304 : /* this should never be triggered! */
305 0 : ntp_signd_terminate_connection(ntp_signd_conn,
306 : "ntp_signd_send: called");
307 0 : }
308 :
309 : struct ntp_signd_call {
310 : struct ntp_signd_connection *ntp_signd_conn;
311 : DATA_BLOB in;
312 : DATA_BLOB out;
313 : uint8_t out_hdr[4];
314 : struct iovec out_iov[2];
315 : };
316 :
317 : static void ntp_signd_call_writev_done(struct tevent_req *subreq);
318 :
319 2 : static void ntp_signd_call_loop(struct tevent_req *subreq)
320 : {
321 2 : struct ntp_signd_connection *ntp_signd_conn = tevent_req_callback_data(subreq,
322 : struct ntp_signd_connection);
323 0 : struct ntp_signd_call *call;
324 0 : NTSTATUS status;
325 :
326 2 : call = talloc(ntp_signd_conn, struct ntp_signd_call);
327 2 : if (call == NULL) {
328 0 : ntp_signd_terminate_connection(ntp_signd_conn,
329 : "ntp_signd_call_loop: "
330 : "no memory for ntp_signd_call");
331 1 : return;
332 : }
333 2 : call->ntp_signd_conn = ntp_signd_conn;
334 :
335 2 : status = tstream_read_pdu_blob_recv(subreq,
336 : call,
337 : &call->in);
338 2 : TALLOC_FREE(subreq);
339 2 : if (!NT_STATUS_IS_OK(status)) {
340 0 : const char *reason;
341 :
342 1 : reason = talloc_asprintf(call, "ntp_signd_call_loop: "
343 : "tstream_read_pdu_blob_recv() - %s",
344 : nt_errstr(status));
345 1 : if (reason == NULL) {
346 0 : reason = nt_errstr(status);
347 : }
348 :
349 1 : ntp_signd_terminate_connection(ntp_signd_conn, reason);
350 1 : return;
351 : }
352 :
353 1 : DEBUG(10,("Received NTP TCP packet of length %lu from %s\n",
354 : (long) call->in.length,
355 : tsocket_address_string(ntp_signd_conn->conn->remote_address, call)));
356 :
357 : /* skip length header */
358 1 : call->in.data +=4;
359 1 : call->in.length -= 4;
360 :
361 1 : status = ntp_signd_process(ntp_signd_conn,
362 : call,
363 : &call->in,
364 : &call->out);
365 1 : if (! NT_STATUS_IS_OK(status)) {
366 0 : const char *reason;
367 :
368 0 : reason = talloc_asprintf(call, "ntp_signd_process failed: %s",
369 : nt_errstr(status));
370 0 : if (reason == NULL) {
371 0 : reason = nt_errstr(status);
372 : }
373 :
374 0 : ntp_signd_terminate_connection(ntp_signd_conn, reason);
375 0 : return;
376 : }
377 :
378 : /* First add the length of the out buffer */
379 1 : RSIVAL(call->out_hdr, 0, call->out.length);
380 1 : call->out_iov[0].iov_base = (char *) call->out_hdr;
381 1 : call->out_iov[0].iov_len = 4;
382 :
383 1 : call->out_iov[1].iov_base = (char *) call->out.data;
384 1 : call->out_iov[1].iov_len = call->out.length;
385 :
386 1 : subreq = tstream_writev_queue_send(call,
387 1 : ntp_signd_conn->conn->event.ctx,
388 : ntp_signd_conn->tstream,
389 : ntp_signd_conn->send_queue,
390 1 : call->out_iov, 2);
391 1 : if (subreq == NULL) {
392 0 : ntp_signd_terminate_connection(ntp_signd_conn, "ntp_signd_call_loop: "
393 : "no memory for tstream_writev_queue_send");
394 0 : return;
395 : }
396 :
397 1 : tevent_req_set_callback(subreq, ntp_signd_call_writev_done, call);
398 :
399 : /*
400 : * The NTP tcp pdu's has the length as 4 byte (initial_read_size),
401 : * tstream_full_request_u32 provides the pdu length then.
402 : */
403 1 : subreq = tstream_read_pdu_blob_send(ntp_signd_conn,
404 1 : ntp_signd_conn->conn->event.ctx,
405 : ntp_signd_conn->tstream,
406 : 4, /* initial_read_size */
407 : tstream_full_request_u32,
408 : ntp_signd_conn);
409 1 : if (subreq == NULL) {
410 0 : ntp_signd_terminate_connection(ntp_signd_conn, "ntp_signd_call_loop: "
411 : "no memory for tstream_read_pdu_blob_send");
412 0 : return;
413 : }
414 1 : tevent_req_set_callback(subreq, ntp_signd_call_loop, ntp_signd_conn);
415 : }
416 :
417 1 : static void ntp_signd_call_writev_done(struct tevent_req *subreq)
418 : {
419 1 : struct ntp_signd_call *call = tevent_req_callback_data(subreq,
420 : struct ntp_signd_call);
421 0 : int sys_errno;
422 0 : int rc;
423 :
424 1 : rc = tstream_writev_queue_recv(subreq, &sys_errno);
425 1 : TALLOC_FREE(subreq);
426 1 : if (rc == -1) {
427 0 : const char *reason;
428 :
429 0 : reason = talloc_asprintf(call, "ntp_signd_call_writev_done: "
430 : "tstream_writev_queue_recv() - %d:%s",
431 : sys_errno, strerror(sys_errno));
432 0 : if (!reason) {
433 0 : reason = "ntp_signd_call_writev_done: "
434 : "tstream_writev_queue_recv() failed";
435 : }
436 :
437 0 : ntp_signd_terminate_connection(call->ntp_signd_conn, reason);
438 0 : return;
439 : }
440 :
441 : /* We don't care about errors */
442 :
443 1 : talloc_free(call);
444 : }
445 :
446 : /*
447 : called when we get a new connection
448 : */
449 1 : static void ntp_signd_accept(struct stream_connection *conn)
450 : {
451 1 : struct ntp_signd_server *ntp_signd = talloc_get_type(conn->private_data,
452 : struct ntp_signd_server);
453 0 : struct ntp_signd_connection *ntp_signd_conn;
454 0 : struct tevent_req *subreq;
455 0 : int rc;
456 :
457 1 : ntp_signd_conn = talloc_zero(conn, struct ntp_signd_connection);
458 1 : if (ntp_signd_conn == NULL) {
459 0 : stream_terminate_connection(conn,
460 : "ntp_signd_accept: out of memory");
461 0 : return;
462 : }
463 :
464 1 : ntp_signd_conn->send_queue = tevent_queue_create(conn,
465 : "ntp_signd_accept");
466 1 : if (ntp_signd_conn->send_queue == NULL) {
467 0 : stream_terminate_connection(conn,
468 : "ntp_signd_accept: out of memory");
469 0 : return;
470 : }
471 :
472 1 : TALLOC_FREE(conn->event.fde);
473 :
474 1 : rc = tstream_bsd_existing_socket(ntp_signd_conn,
475 : socket_get_fd(conn->socket),
476 : &ntp_signd_conn->tstream);
477 1 : if (rc < 0) {
478 0 : stream_terminate_connection(conn,
479 : "ntp_signd_accept: out of memory");
480 0 : return;
481 : }
482 : /* as server we want to fail early */
483 1 : tstream_bsd_fail_readv_first_error(ntp_signd_conn->tstream, true);
484 :
485 1 : ntp_signd_conn->conn = conn;
486 1 : ntp_signd_conn->ntp_signd = ntp_signd;
487 1 : conn->private_data = ntp_signd_conn;
488 :
489 : /*
490 : * The NTP tcp pdu's has the length as 4 byte (initial_read_size),
491 : * tstream_full_request_u32 provides the pdu length then.
492 : */
493 1 : subreq = tstream_read_pdu_blob_send(ntp_signd_conn,
494 1 : ntp_signd_conn->conn->event.ctx,
495 : ntp_signd_conn->tstream,
496 : 4, /* initial_read_size */
497 : tstream_full_request_u32,
498 : ntp_signd_conn);
499 1 : if (subreq == NULL) {
500 0 : ntp_signd_terminate_connection(ntp_signd_conn,
501 : "ntp_signd_accept: "
502 : "no memory for tstream_read_pdu_blob_send");
503 0 : return;
504 : }
505 1 : tevent_req_set_callback(subreq, ntp_signd_call_loop, ntp_signd_conn);
506 : }
507 :
508 : static const struct stream_server_ops ntp_signd_stream_ops = {
509 : .name = "ntp_signd",
510 : .accept_connection = ntp_signd_accept,
511 : .recv_handler = ntp_signd_recv,
512 : .send_handler = ntp_signd_send
513 : };
514 :
515 : /*
516 : startup the ntp_signd task
517 : */
518 65 : static NTSTATUS ntp_signd_task_init(struct task_server *task)
519 : {
520 2 : struct ntp_signd_server *ntp_signd;
521 2 : NTSTATUS status;
522 :
523 2 : const char *address;
524 :
525 65 : if (!directory_create_or_exist_strict(lpcfg_ntp_signd_socket_directory(task->lp_ctx), geteuid(), 0750)) {
526 0 : char *error = talloc_asprintf(task, "Cannot create NTP signd pipe directory: %s",
527 : lpcfg_ntp_signd_socket_directory(task->lp_ctx));
528 0 : task_server_terminate(task,
529 : error, true);
530 0 : return NT_STATUS_UNSUCCESSFUL;
531 : }
532 :
533 65 : task_server_set_title(task, "task[ntp_signd]");
534 :
535 65 : ntp_signd = talloc(task, struct ntp_signd_server);
536 65 : if (ntp_signd == NULL) {
537 0 : task_server_terminate(task, "ntp_signd: out of memory", true);
538 0 : return NT_STATUS_NO_MEMORY;
539 : }
540 :
541 65 : ntp_signd->task = task;
542 :
543 : /* Must be system to get at the password hashes */
544 65 : ntp_signd->samdb = samdb_connect(ntp_signd,
545 : task->event_ctx,
546 : task->lp_ctx,
547 : system_session(task->lp_ctx),
548 : NULL,
549 : 0);
550 65 : if (ntp_signd->samdb == NULL) {
551 0 : task_server_terminate(task, "ntp_signd failed to open samdb", true);
552 0 : return NT_STATUS_UNSUCCESSFUL;
553 : }
554 :
555 65 : address = talloc_asprintf(ntp_signd, "%s/socket", lpcfg_ntp_signd_socket_directory(task->lp_ctx));
556 65 : if (address == NULL) {
557 0 : task_server_terminate(
558 : task, "ntp_signd out of memory in talloc_asprintf()", true);
559 0 : return NT_STATUS_NO_MEMORY;
560 : }
561 :
562 130 : status = stream_setup_socket(ntp_signd->task,
563 63 : ntp_signd->task->event_ctx,
564 65 : ntp_signd->task->lp_ctx,
565 : task->model_ops,
566 : &ntp_signd_stream_ops,
567 : "unix", address, NULL,
568 63 : lpcfg_socket_options(ntp_signd->task->lp_ctx),
569 : ntp_signd,
570 65 : ntp_signd->task->process_context);
571 65 : if (!NT_STATUS_IS_OK(status)) {
572 0 : DEBUG(0,("Failed to bind to %s - %s\n",
573 : address, nt_errstr(status)));
574 0 : return status;
575 : }
576 :
577 65 : return NT_STATUS_OK;
578 :
579 : }
580 :
581 :
582 : /* called at smbd startup - register ourselves as a server service */
583 66 : NTSTATUS server_service_ntp_signd_init(TALLOC_CTX *ctx)
584 : {
585 3 : static const struct service_details details = {
586 : .inhibit_fork_on_accept = true,
587 : .inhibit_pre_fork = true,
588 : .task_init = ntp_signd_task_init,
589 : .post_fork = NULL
590 : };
591 66 : return register_server_service(ctx, "ntp_signd", &details);
592 : }
|