Line data Source code
1 : /*
2 : * Copyright (c) 2020 Andreas Schneider <asn@samba.org>
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "includes.h"
19 : #include "lib/param/param.h"
20 : #include "dynconfig/dynconfig.h"
21 : #include "auth/gensec/gensec.h"
22 : #include "libcli/smb/smb_util.h"
23 : #include "cmdline_private.h"
24 :
25 : #include <samba/version.h>
26 :
27 : static TALLOC_CTX *cmdline_mem_ctx;
28 : static struct loadparm_context *cmdline_lp_ctx;
29 : static struct cli_credentials *cmdline_creds;
30 : static samba_cmdline_load_config cmdline_load_config_fn;
31 : static struct samba_cmdline_daemon_cfg cmdline_daemon_cfg;
32 :
33 : static NTSTATUS (*cli_credentials_set_machine_account_fn)(
34 : struct cli_credentials *cred,
35 : struct loadparm_context *lp_ctx) =
36 : cli_credentials_set_machine_account;
37 :
38 : /* PRIVATE */
39 8180 : bool samba_cmdline_set_talloc_ctx(TALLOC_CTX *mem_ctx)
40 : {
41 8180 : if (cmdline_mem_ctx != NULL) {
42 0 : return false;
43 : }
44 :
45 8180 : cmdline_mem_ctx = mem_ctx;
46 8180 : return true;
47 : }
48 :
49 29707 : TALLOC_CTX *samba_cmdline_get_talloc_ctx(void)
50 : {
51 29707 : return cmdline_mem_ctx;
52 : }
53 :
54 6 : static void _samba_cmdline_talloc_log(const char *message)
55 : {
56 6 : D_ERR("%s", message);
57 6 : }
58 :
59 8180 : bool samba_cmdline_init_common(TALLOC_CTX *mem_ctx)
60 : {
61 : bool ok;
62 :
63 8180 : ok = samba_cmdline_set_talloc_ctx(mem_ctx);
64 8180 : if (!ok) {
65 0 : return false;
66 : }
67 :
68 8180 : cmdline_daemon_cfg = (struct samba_cmdline_daemon_cfg) {
69 : .fork = true,
70 : };
71 :
72 8180 : fault_setup();
73 :
74 : /*
75 : * Log to stderr by default.
76 : * This can be changed to stdout using the option: --debug-stdout
77 : */
78 8180 : setup_logging(getprogname(), DEBUG_DEFAULT_STDERR);
79 :
80 8180 : talloc_set_log_fn(_samba_cmdline_talloc_log);
81 8180 : talloc_set_abort_fn(smb_panic);
82 :
83 8180 : return true;
84 : }
85 :
86 8180 : bool samba_cmdline_set_load_config_fn(samba_cmdline_load_config fn)
87 : {
88 8180 : cmdline_load_config_fn = fn;
89 8180 : return true;
90 : }
91 :
92 : /* PUBLIC */
93 8180 : bool samba_cmdline_set_lp_ctx(struct loadparm_context *lp_ctx)
94 : {
95 8180 : if (lp_ctx == NULL) {
96 0 : return false;
97 : }
98 8180 : cmdline_lp_ctx = lp_ctx;
99 :
100 8180 : return true;
101 : }
102 :
103 57651 : struct loadparm_context *samba_cmdline_get_lp_ctx(void)
104 : {
105 57651 : return cmdline_lp_ctx;
106 : }
107 :
108 8180 : bool samba_cmdline_set_creds(struct cli_credentials *creds)
109 : {
110 8180 : if (creds == NULL) {
111 0 : return false;
112 : }
113 :
114 8180 : TALLOC_FREE(cmdline_creds);
115 8180 : cmdline_creds = creds;
116 :
117 8180 : return true;
118 : }
119 :
120 1548553 : struct cli_credentials *samba_cmdline_get_creds(void)
121 : {
122 1548553 : return cmdline_creds;
123 : }
124 :
125 3722 : struct samba_cmdline_daemon_cfg *samba_cmdline_get_daemon_cfg(void)
126 : {
127 3722 : return &cmdline_daemon_cfg;
128 : }
129 :
130 5048 : void samba_cmdline_set_machine_account_fn(
131 : NTSTATUS (*fn) (struct cli_credentials *cred,
132 : struct loadparm_context *lp_ctx))
133 : {
134 5048 : cli_credentials_set_machine_account_fn = fn;
135 5048 : }
136 :
137 13691 : bool samba_cmdline_burn(int argc, char *argv[])
138 : {
139 13691 : bool burnt = false;
140 13691 : bool found = false;
141 13691 : bool is_user = false;
142 13691 : char *p = NULL;
143 : int i;
144 13691 : size_t ulen = 0;
145 :
146 79804 : for (i = 0; i < argc; i++) {
147 66113 : p = argv[i];
148 66113 : if (p == NULL) {
149 0 : return false;
150 : }
151 :
152 : /*
153 : * Take care that this list must be in longest-match
154 : * first order
155 : */
156 66113 : if (strncmp(p, "-U", 2) == 0) {
157 4557 : ulen = 2;
158 4557 : found = true;
159 4557 : is_user = true;
160 61556 : } else if (strncmp(p, "--user", 6) == 0) {
161 0 : ulen = 6;
162 0 : found = true;
163 0 : is_user = true;
164 61556 : } else if (strncmp(p, "--password2", 11) == 0) {
165 0 : ulen = 11;
166 0 : found = true;
167 61556 : } else if (strncmp(p, "--password", 10) == 0) {
168 378 : ulen = 10;
169 378 : found = true;
170 61178 : } else if (strncmp(p, "--newpassword", 13) == 0) {
171 22 : ulen = 13;
172 22 : found = true;
173 : }
174 :
175 66113 : if (found) {
176 5055 : char *q = NULL;
177 :
178 5055 : if (strlen(p) == ulen) {
179 98 : continue;
180 : }
181 :
182 4957 : if (is_user) {
183 4557 : q = strchr_m(p, '%');
184 4557 : if (q != NULL) {
185 4165 : p = q;
186 : }
187 : } else {
188 400 : p += ulen;
189 : }
190 :
191 4957 : memset_s(p, strlen(p), '\0', strlen(p));
192 4957 : found = false;
193 4957 : is_user = false;
194 4957 : burnt = true;
195 : }
196 : }
197 13691 : return burnt;
198 : }
199 :
200 25028730 : static bool is_popt_table_end(const struct poptOption *o)
201 : {
202 25028730 : if (o->longName == NULL &&
203 5764076 : o->shortName == 0 &&
204 5695326 : o->argInfo == 0 &&
205 2150064 : o->arg == NULL &&
206 2150064 : o->val == 0 &&
207 2150064 : o->descrip == NULL &&
208 2150064 : o->argDescrip == NULL) {
209 2150064 : return true;
210 : }
211 :
212 22878666 : return false;
213 : }
214 :
215 2098901 : static void find_duplicates(const struct poptOption *needle,
216 : const struct poptOption *haystack,
217 : size_t *count)
218 : {
219 2098901 : for(;
220 24525735 : !is_popt_table_end(haystack);
221 22426834 : haystack++) {
222 22426834 : switch (haystack->argInfo) {
223 1730777 : case POPT_ARG_INCLUDE_TABLE:
224 1730777 : if (haystack->arg != NULL) {
225 1730777 : find_duplicates(needle, haystack->arg, count);
226 : }
227 :
228 1730777 : break;
229 20696057 : default:
230 20696057 : if (needle->shortName != 0 &&
231 9481187 : needle->shortName == haystack->shortName) {
232 173987 : (*count)++;
233 173987 : break;
234 : }
235 :
236 20522070 : if (needle->longName != NULL &&
237 39107113 : haystack->longName != NULL &&
238 18658793 : strequal(needle->longName, haystack->longName)) {
239 194137 : (*count)++;
240 194137 : break;
241 : }
242 20327933 : break;
243 : }
244 :
245 22426834 : if (*count > 1) {
246 0 : return;
247 : }
248 : }
249 : }
250 :
251 51163 : static bool cmdline_sanity_checker(const struct poptOption *current_opts,
252 : const struct poptOption *full_opts)
253 : {
254 51163 : const struct poptOption *o = current_opts;
255 :
256 51163 : for(;
257 502995 : !is_popt_table_end(o);
258 451832 : o++) {
259 : bool ok;
260 :
261 451832 : switch (o->argInfo) {
262 41854 : case POPT_ARG_INCLUDE_TABLE:
263 41854 : if (o->arg != NULL) {
264 41854 : ok = cmdline_sanity_checker(o->arg, full_opts);
265 41854 : if (!ok) {
266 0 : return false;
267 : }
268 : }
269 :
270 41854 : break;
271 409978 : default:
272 409978 : if (o->longName != NULL || o->shortName != 0) {
273 368124 : size_t count = 0;
274 :
275 368124 : find_duplicates(o, full_opts, &count);
276 368124 : if (count > 1) {
277 0 : DBG_ERR("Duplicate option '--%s|-%c' "
278 : "detected!\n",
279 : o->longName,
280 : o->shortName != 0 ?
281 : o->shortName :
282 : '-');
283 0 : return false;
284 : }
285 : }
286 :
287 409978 : break;
288 : }
289 : }
290 :
291 51163 : return true;
292 : }
293 :
294 9309 : bool samba_cmdline_sanity_check(const struct poptOption *opts)
295 : {
296 9309 : return cmdline_sanity_checker(opts, opts);
297 : }
298 :
299 8059 : poptContext samba_popt_get_context(const char * name,
300 : int argc, const char ** argv,
301 : const struct poptOption * options,
302 : unsigned int flags)
303 : {
304 : #ifdef DEVELOPER
305 : bool ok;
306 :
307 8059 : ok = samba_cmdline_sanity_check(options);
308 8059 : if (!ok) {
309 0 : return NULL;
310 : }
311 : #endif
312 8059 : return poptGetContext(name, argc, argv, options, flags);
313 : }
314 :
315 : /**********************************************************
316 : * COMMON SAMBA POPT
317 : **********************************************************/
318 :
319 : static bool log_to_file;
320 :
321 10524 : static bool set_logfile(TALLOC_CTX *mem_ctx,
322 : struct loadparm_context *lp_ctx,
323 : const char *log_basename,
324 : const char *process_name,
325 : bool from_cmdline)
326 : {
327 10524 : bool ok = false;
328 10524 : char *new_logfile = talloc_asprintf(mem_ctx,
329 : "%s/log.%s",
330 : log_basename,
331 : process_name);
332 10524 : if (new_logfile == NULL) {
333 0 : return false;
334 : }
335 :
336 10524 : if (from_cmdline) {
337 181 : ok = lpcfg_set_cmdline(lp_ctx,
338 : "log file",
339 : new_logfile);
340 : } else {
341 10343 : ok = lpcfg_do_global_parameter(lp_ctx,
342 : "log file",
343 : new_logfile);
344 : }
345 10524 : if (!ok) {
346 0 : fprintf(stderr,
347 : "Failed to set log to %s\n",
348 : new_logfile);
349 0 : TALLOC_FREE(new_logfile);
350 0 : return false;
351 : }
352 10524 : debug_set_logfile(new_logfile);
353 10524 : TALLOC_FREE(new_logfile);
354 :
355 10524 : return true;
356 : }
357 :
358 29707 : static void popt_samba_callback(poptContext popt_ctx,
359 : enum poptCallbackReason reason,
360 : const struct poptOption *opt,
361 : const char *arg, const void *data)
362 : {
363 29707 : TALLOC_CTX *mem_ctx = samba_cmdline_get_talloc_ctx();
364 29707 : struct loadparm_context *lp_ctx = samba_cmdline_get_lp_ctx();
365 29707 : const char *pname = NULL;
366 : bool ok;
367 :
368 : /* Find out basename of current program */
369 29707 : pname = getprogname();
370 :
371 29707 : if (reason == POPT_CALLBACK_REASON_PRE) {
372 10343 : if (lp_ctx == NULL) {
373 0 : fprintf(stderr,
374 : "Command line parsing not initialized!\n");
375 0 : exit(1);
376 : }
377 10343 : ok = set_logfile(mem_ctx,
378 : lp_ctx,
379 : get_dyn_LOGFILEBASE(),
380 : pname,
381 : false);
382 10343 : if (!ok) {
383 0 : fprintf(stderr,
384 : "Failed to set log file for %s\n",
385 : pname);
386 0 : exit(1);
387 : }
388 10343 : return;
389 : }
390 :
391 19364 : if (reason == POPT_CALLBACK_REASON_POST) {
392 10284 : ok = cmdline_load_config_fn();
393 10284 : if (!ok) {
394 0 : fprintf(stderr,
395 : "%s - Failed to load config file!\n",
396 : getprogname());
397 0 : exit(1);
398 : }
399 :
400 10284 : if (log_to_file) {
401 : const struct loadparm_substitution *lp_sub =
402 181 : lpcfg_noop_substitution();
403 181 : char *logfile = NULL;
404 :
405 181 : logfile = lpcfg_logfile(lp_ctx, lp_sub, mem_ctx);
406 181 : if (logfile == NULL) {
407 0 : fprintf(stderr,
408 : "Failed to setup logging to file!");
409 0 : exit(1);
410 : }
411 181 : debug_set_logfile(logfile);
412 181 : setup_logging(logfile, DEBUG_FILE);
413 181 : TALLOC_FREE(logfile);
414 : }
415 :
416 10284 : return;
417 : }
418 :
419 9080 : switch(opt->val) {
420 0 : case OPT_LEAK_REPORT:
421 0 : talloc_enable_leak_report();
422 0 : break;
423 0 : case OPT_LEAK_REPORT_FULL:
424 0 : talloc_enable_leak_report_full();
425 0 : break;
426 4788 : case OPT_OPTION:
427 4788 : if (arg != NULL) {
428 4788 : ok = lpcfg_set_option(lp_ctx, arg);
429 4788 : if (!ok) {
430 1 : fprintf(stderr, "Error setting option '%s'\n", arg);
431 1 : exit(1);
432 : }
433 : }
434 4787 : break;
435 131 : case 'd':
436 131 : if (arg != NULL) {
437 131 : ok = lpcfg_set_cmdline(lp_ctx, "log level", arg);
438 131 : if (!ok) {
439 0 : fprintf(stderr,
440 : "Failed to set debug level to: %s\n",
441 : arg);
442 0 : exit(1);
443 : }
444 : }
445 131 : break;
446 61 : case OPT_DEBUG_STDOUT:
447 61 : setup_logging(pname, DEBUG_STDOUT);
448 61 : break;
449 3919 : case OPT_CONFIGFILE:
450 3919 : if (arg != NULL) {
451 3919 : set_dyn_CONFIGFILE(arg);
452 : }
453 3919 : break;
454 181 : case 'l':
455 181 : if (arg != NULL) {
456 181 : ok = set_logfile(mem_ctx, lp_ctx, arg, pname, true);
457 181 : if (!ok) {
458 0 : fprintf(stderr,
459 : "Failed to set log file for %s\n",
460 : arg);
461 0 : exit(1);
462 : }
463 181 : log_to_file = true;
464 :
465 181 : set_dyn_LOGFILEBASE(arg);
466 : }
467 181 : break;
468 : }
469 : }
470 :
471 : static struct poptOption popt_common_debug[] = {
472 : {
473 : .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
474 : .arg = (void *)popt_samba_callback,
475 : },
476 : {
477 : .longName = "debuglevel",
478 : .shortName = 'd',
479 : .argInfo = POPT_ARG_STRING,
480 : .val = 'd',
481 : .descrip = "Set debug level",
482 : .argDescrip = "DEBUGLEVEL",
483 : },
484 : {
485 : .longName = "debug-stdout",
486 : .argInfo = POPT_ARG_NONE,
487 : .val = OPT_DEBUG_STDOUT,
488 : .descrip = "Send debug output to standard output",
489 : },
490 : POPT_TABLEEND
491 : };
492 :
493 : static struct poptOption popt_common_option[] = {
494 : {
495 : .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
496 : .arg = (void *)popt_samba_callback,
497 : },
498 : {
499 : .longName = "option",
500 : .argInfo = POPT_ARG_STRING,
501 : .val = OPT_OPTION,
502 : .descrip = "Set smb.conf option from command line",
503 : .argDescrip = "name=value",
504 : },
505 : POPT_TABLEEND
506 : };
507 :
508 : static struct poptOption popt_common_config[] = {
509 : {
510 : .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
511 : .arg = (void *)popt_samba_callback,
512 : },
513 : {
514 : .longName = "configfile",
515 : .argInfo = POPT_ARG_STRING,
516 : .val = OPT_CONFIGFILE,
517 : .descrip = "Use alternative configuration file",
518 : .argDescrip = "CONFIGFILE",
519 : },
520 : POPT_TABLEEND
521 : };
522 :
523 : static struct poptOption popt_common_samba[] = {
524 : {
525 : .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
526 : .arg = (void *)popt_samba_callback,
527 : },
528 : {
529 : .longName = "debuglevel",
530 : .shortName = 'd',
531 : .argInfo = POPT_ARG_STRING,
532 : .val = 'd',
533 : .descrip = "Set debug level",
534 : .argDescrip = "DEBUGLEVEL",
535 : },
536 : {
537 : .longName = "debug-stdout",
538 : .argInfo = POPT_ARG_NONE,
539 : .val = OPT_DEBUG_STDOUT,
540 : .descrip = "Send debug output to standard output",
541 : },
542 : {
543 : .longName = "configfile",
544 : .shortName = 's',
545 : .argInfo = POPT_ARG_STRING,
546 : .val = OPT_CONFIGFILE,
547 : .descrip = "Use alternative configuration file",
548 : .argDescrip = "CONFIGFILE",
549 : },
550 : {
551 : .longName = "option",
552 : .argInfo = POPT_ARG_STRING,
553 : .val = OPT_OPTION,
554 : .descrip = "Set smb.conf option from command line",
555 : .argDescrip = "name=value",
556 : },
557 : {
558 : .longName = "log-basename",
559 : .shortName = 'l',
560 : .argInfo = POPT_ARG_STRING,
561 : .val = 'l',
562 : .descrip = "Basename for log/debug files",
563 : .argDescrip = "LOGFILEBASE",
564 : },
565 : {
566 : .longName = "leak-report",
567 : .argInfo = POPT_ARG_NONE,
568 : .val = OPT_LEAK_REPORT,
569 : .descrip = "enable talloc leak reporting on exit",
570 : },
571 : {
572 : .longName = "leak-report-full",
573 : .argInfo = POPT_ARG_NONE,
574 : .val = OPT_LEAK_REPORT_FULL,
575 : .descrip = "enable full talloc leak reporting on exit",
576 : },
577 : POPT_TABLEEND
578 : };
579 :
580 : static struct poptOption popt_common_samba_ldb[] = {
581 : {
582 : .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
583 : .arg = (void *)popt_samba_callback,
584 : },
585 : {
586 : .longName = "debuglevel",
587 : .shortName = 'd',
588 : .argInfo = POPT_ARG_STRING,
589 : .val = 'd',
590 : .descrip = "Set debug level",
591 : .argDescrip = "DEBUGLEVEL",
592 : },
593 : {
594 : .longName = "debug-stdout",
595 : .argInfo = POPT_ARG_NONE,
596 : .val = OPT_DEBUG_STDOUT,
597 : .descrip = "Send debug output to standard output",
598 : },
599 : {
600 : .longName = "configfile",
601 : .argInfo = POPT_ARG_STRING,
602 : .val = OPT_CONFIGFILE,
603 : .descrip = "Use alternative configuration file",
604 : .argDescrip = "CONFIGFILE",
605 : },
606 : {
607 : .longName = "option",
608 : .argInfo = POPT_ARG_STRING,
609 : .val = OPT_OPTION,
610 : .descrip = "Set smb.conf option from command line",
611 : .argDescrip = "name=value",
612 : },
613 : {
614 : .longName = "log-basename",
615 : .shortName = 'l',
616 : .argInfo = POPT_ARG_STRING,
617 : .val = 'l',
618 : .descrip = "Basename for log/debug files",
619 : .argDescrip = "LOGFILEBASE",
620 : },
621 : {
622 : .longName = "leak-report",
623 : .argInfo = POPT_ARG_NONE,
624 : .val = OPT_LEAK_REPORT,
625 : .descrip = "enable talloc leak reporting on exit",
626 : },
627 : {
628 : .longName = "leak-report-full",
629 : .argInfo = POPT_ARG_NONE,
630 : .val = OPT_LEAK_REPORT_FULL,
631 : .descrip = "enable full talloc leak reporting on exit",
632 : },
633 : POPT_TABLEEND
634 : };
635 :
636 : /**********************************************************
637 : * CONNECTION POPT
638 : **********************************************************/
639 :
640 6139 : static void popt_connection_callback(poptContext popt_ctx,
641 : enum poptCallbackReason reason,
642 : const struct poptOption *opt,
643 : const char *arg,
644 : const void *data)
645 : {
646 6139 : struct loadparm_context *lp_ctx = cmdline_lp_ctx;
647 :
648 6139 : if (reason == POPT_CALLBACK_REASON_PRE) {
649 4796 : if (lp_ctx == NULL) {
650 0 : fprintf(stderr,
651 : "Command line parsing not initialized!\n");
652 0 : exit(1);
653 : }
654 4796 : return;
655 : }
656 :
657 1343 : switch(opt->val) {
658 0 : case 'O':
659 0 : if (arg != NULL) {
660 0 : lpcfg_set_cmdline(lp_ctx, "socket options", arg);
661 : }
662 0 : break;
663 0 : case 'R':
664 0 : if (arg != NULL) {
665 0 : lpcfg_set_cmdline(lp_ctx, "name resolve order", arg);
666 : }
667 0 : break;
668 528 : case 'm':
669 528 : if (arg != NULL) {
670 528 : lpcfg_set_cmdline(lp_ctx, "client max protocol", arg);
671 : }
672 528 : break;
673 0 : case OPT_NETBIOS_SCOPE:
674 0 : if (arg != NULL) {
675 0 : lpcfg_set_cmdline(lp_ctx, "netbios scope", arg);
676 : }
677 0 : break;
678 8 : case 'n':
679 8 : if (arg != NULL) {
680 8 : lpcfg_set_cmdline(lp_ctx, "netbios name", arg);
681 : }
682 8 : break;
683 782 : case 'W':
684 782 : if (arg != NULL) {
685 782 : lpcfg_set_cmdline(lp_ctx, "workgroup", arg);
686 : }
687 782 : break;
688 25 : case 'r':
689 25 : if (arg != NULL) {
690 25 : lpcfg_set_cmdline(lp_ctx, "realm", arg);
691 : }
692 25 : break;
693 : }
694 : }
695 :
696 : static struct poptOption popt_common_connection[] = {
697 : {
698 : .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE,
699 : .arg = (void *)popt_connection_callback,
700 : },
701 : {
702 : .longName = "name-resolve",
703 : .shortName = 'R',
704 : .argInfo = POPT_ARG_STRING,
705 : .val = 'R',
706 : .descrip = "Use these name resolution services only",
707 : .argDescrip = "NAME-RESOLVE-ORDER",
708 : },
709 : {
710 : .longName = "socket-options",
711 : .shortName = 'O',
712 : .argInfo = POPT_ARG_STRING,
713 : .val = 'O',
714 : .descrip = "socket options to use",
715 : .argDescrip = "SOCKETOPTIONS",
716 : },
717 : {
718 : .longName = "max-protocol",
719 : .shortName = 'm',
720 : .argInfo = POPT_ARG_STRING,
721 : .val = 'm',
722 : .descrip = "Set max protocol level",
723 : .argDescrip = "MAXPROTOCOL",
724 : },
725 : {
726 : .longName = "netbiosname",
727 : .shortName = 'n',
728 : .argInfo = POPT_ARG_STRING,
729 : .val = 'n',
730 : .descrip = "Primary netbios name",
731 : .argDescrip = "NETBIOSNAME",
732 : },
733 : {
734 : .longName = "netbios-scope",
735 : .argInfo = POPT_ARG_STRING,
736 : .val = OPT_NETBIOS_SCOPE,
737 : .descrip = "Use this Netbios scope",
738 : .argDescrip = "SCOPE",
739 : },
740 : {
741 : .longName = "workgroup",
742 : .shortName = 'W',
743 : .argInfo = POPT_ARG_STRING,
744 : .val = 'W',
745 : .descrip = "Set the workgroup name",
746 : .argDescrip = "WORKGROUP",
747 : },
748 : {
749 : .longName = "realm",
750 : .argInfo = POPT_ARG_STRING,
751 : .val = 'r',
752 : .descrip = "Set the realm name",
753 : .argDescrip = "REALM",
754 : },
755 : POPT_TABLEEND
756 : };
757 :
758 : /**********************************************************
759 : * CREDENTIALS POPT
760 : **********************************************************/
761 :
762 : static bool skip_password_callback;
763 : static bool machine_account_pending;
764 :
765 13419 : static void popt_common_credentials_callback(poptContext popt_ctx,
766 : enum poptCallbackReason reason,
767 : const struct poptOption *opt,
768 : const char *arg,
769 : const void *data)
770 : {
771 13419 : struct loadparm_context *lp_ctx = samba_cmdline_get_lp_ctx();
772 13419 : struct cli_credentials *creds = samba_cmdline_get_creds();
773 : bool ok;
774 :
775 13419 : if (reason == POPT_CALLBACK_REASON_PRE) {
776 4618 : if (creds == NULL) {
777 0 : fprintf(stderr,
778 : "Command line parsing not initialized!\n");
779 0 : exit(1);
780 : }
781 4618 : return;
782 : }
783 :
784 8801 : if (reason == POPT_CALLBACK_REASON_POST) {
785 4600 : const char *username = NULL;
786 4600 : enum credentials_obtained username_obtained =
787 : CRED_UNINITIALISED;
788 4600 : enum credentials_obtained password_obtained =
789 : CRED_UNINITIALISED;
790 :
791 : /*
792 : * This calls cli_credentials_set_conf() to get the defaults
793 : * form smb.conf and set the winbind separator.
794 : *
795 : * Just warn that we can't read the smb.conf. The might not be
796 : * one available or we want to ignore it.
797 : */
798 4600 : ok = cli_credentials_guess(creds, lp_ctx);
799 4600 : if (!ok) {
800 0 : fprintf(stderr,
801 : "Unable to read defaults from smb.conf\n");
802 : }
803 :
804 4600 : (void)cli_credentials_get_password_and_obtained(creds,
805 : &password_obtained);
806 4600 : if (!skip_password_callback &&
807 4231 : password_obtained < CRED_CALLBACK) {
808 1207 : ok = cli_credentials_set_cmdline_callbacks(creds);
809 1207 : if (!ok) {
810 0 : fprintf(stderr,
811 : "Failed to set cmdline password "
812 : "callback\n");
813 0 : exit(1);
814 : }
815 : }
816 :
817 4600 : if (machine_account_pending) {
818 : NTSTATUS status;
819 :
820 78 : status = cli_credentials_set_machine_account_fn(
821 : creds, lp_ctx);
822 78 : if (!NT_STATUS_IS_OK(status)) {
823 0 : fprintf(stderr,
824 : "Failed to set machine account: %s\n",
825 : nt_errstr(status));
826 0 : exit(1);
827 : }
828 : }
829 :
830 : /*
831 : * When we set the username during the handling of the options
832 : * passed to the binary we haven't loaded the config yet. This
833 : * means that we didn't take the 'winbind separator' into
834 : * account.
835 : *
836 : * The username might contain the domain name and thus it
837 : * hasn't been correctly parsed yet. If we have a username we
838 : * need to set it again to run the string parser for the
839 : * username correctly.
840 : */
841 : username =
842 4600 : cli_credentials_get_username_and_obtained(
843 : creds, &username_obtained);
844 4600 : if (username_obtained == CRED_SPECIFIED &&
845 3212 : username != NULL && username[0] != '\0') {
846 3013 : cli_credentials_parse_string(creds,
847 : username,
848 : CRED_SPECIFIED);
849 : }
850 :
851 4600 : return;
852 : }
853 :
854 4201 : switch(opt->val) {
855 3120 : case 'U':
856 3120 : if (arg != NULL) {
857 3120 : cli_credentials_parse_string(creds,
858 : arg,
859 : CRED_SPECIFIED);
860 : }
861 3120 : break;
862 119 : case OPT_PASSWORD:
863 119 : if (arg != NULL) {
864 119 : ok = cli_credentials_set_password(creds,
865 : arg,
866 : CRED_SPECIFIED);
867 119 : if (!ok) {
868 0 : fprintf(stderr,
869 : "Failed to set password!\n");
870 0 : exit(1);
871 : }
872 :
873 119 : skip_password_callback = true;
874 : }
875 119 : break;
876 0 : case OPT_NT_HASH:
877 0 : cli_credentials_set_password_will_be_nt_hash(creds, true);
878 0 : break;
879 14 : case 'A':
880 14 : if (arg != NULL) {
881 14 : ok = cli_credentials_parse_file(creds,
882 : arg,
883 : CRED_SPECIFIED);
884 14 : if (!ok) {
885 0 : fprintf(stderr,
886 : "Failed to set parse authentication file!\n");
887 0 : exit(1);
888 : }
889 14 : skip_password_callback = true;
890 : }
891 14 : break;
892 20 : case 'N':
893 20 : ok = cli_credentials_set_password(creds,
894 : NULL,
895 : CRED_SPECIFIED);
896 20 : if (!ok) {
897 0 : fprintf(stderr,
898 : "Failed to set password!\n");
899 0 : exit(1);
900 : }
901 20 : skip_password_callback = true;
902 20 : break;
903 78 : case 'P':
904 : /*
905 : * Later, after this is all over, get the machine account
906 : * details from the secrets.(l|t)db.
907 : */
908 78 : machine_account_pending = true;
909 78 : break;
910 55 : case OPT_SIMPLE_BIND_DN:
911 55 : if (arg != NULL) {
912 55 : ok = cli_credentials_set_bind_dn(creds, arg);
913 55 : if (!ok) {
914 0 : fprintf(stderr,
915 : "Failed to set bind DN!\n");
916 0 : exit(1);
917 : }
918 : }
919 55 : break;
920 115 : case OPT_USE_KERBEROS: {
921 115 : int32_t use_kerberos = INT_MIN;
922 115 : if (arg == NULL) {
923 0 : fprintf(stderr,
924 : "Failed to parse "
925 : "--use-kerberos=desired|required|off: "
926 : "Missing argument\n");
927 0 : exit(1);
928 : }
929 :
930 115 : use_kerberos = lpcfg_parse_enum_vals("client use kerberos",
931 : arg);
932 115 : if (use_kerberos == INT_MIN) {
933 0 : fprintf(stderr,
934 : "Failed to parse "
935 : "--use-kerberos=desired|required|off: "
936 : "Invalid argument\n");
937 0 : exit(1);
938 : }
939 :
940 115 : ok = cli_credentials_set_kerberos_state(creds,
941 : use_kerberos,
942 : CRED_SPECIFIED);
943 115 : if (!ok) {
944 0 : fprintf(stderr,
945 : "Failed to set Kerberos state to %s!\n", arg);
946 0 : exit(1);
947 : }
948 115 : break;
949 : }
950 195 : case OPT_USE_KERBEROS_CCACHE: {
951 195 : const char *error_string = NULL;
952 : int rc;
953 :
954 195 : if (arg == NULL) {
955 0 : fprintf(stderr,
956 : "Failed to parse --use-krb5-ccache=CCACHE: "
957 : "Missing argument\n");
958 0 : exit(1);
959 : }
960 :
961 195 : ok = cli_credentials_set_kerberos_state(creds,
962 : CRED_USE_KERBEROS_REQUIRED,
963 : CRED_SPECIFIED);
964 195 : if (!ok) {
965 0 : fprintf(stderr,
966 : "Failed to set Kerberos state to %s!\n", arg);
967 0 : exit(1);
968 : }
969 :
970 195 : rc = cli_credentials_set_ccache(creds,
971 : lp_ctx,
972 : arg,
973 : CRED_SPECIFIED,
974 : &error_string);
975 195 : if (rc != 0) {
976 0 : fprintf(stderr,
977 : "Error reading krb5 credentials cache: '%s'"
978 : " - %s\n",
979 : arg,
980 : error_string);
981 0 : exit(1);
982 : }
983 :
984 195 : skip_password_callback = true;
985 195 : break;
986 : }
987 12 : case OPT_USE_WINBIND_CCACHE:
988 : {
989 : uint32_t gensec_features;
990 :
991 12 : gensec_features = cli_credentials_get_gensec_features(creds);
992 12 : gensec_features |= GENSEC_FEATURE_NTLM_CCACHE;
993 :
994 12 : ok = cli_credentials_set_gensec_features(creds,
995 : gensec_features,
996 : CRED_SPECIFIED);
997 12 : if (!ok) {
998 0 : fprintf(stderr,
999 : "Failed to set gensec feature!\n");
1000 0 : exit(1);
1001 : }
1002 :
1003 12 : skip_password_callback = true;
1004 12 : break;
1005 : }
1006 473 : case OPT_CLIENT_PROTECTION: {
1007 : uint32_t gensec_features;
1008 473 : enum smb_signing_setting signing_state =
1009 : SMB_SIGNING_OFF;
1010 473 : enum smb_encryption_setting encryption_state =
1011 : SMB_ENCRYPTION_OFF;
1012 :
1013 473 : if (arg == NULL) {
1014 0 : fprintf(stderr,
1015 : "Failed to parse "
1016 : "--client-protection=sign|encrypt|off: "
1017 : "Missing argument\n");
1018 0 : exit(1);
1019 : }
1020 :
1021 : gensec_features =
1022 473 : cli_credentials_get_gensec_features(
1023 : creds);
1024 :
1025 473 : if (strequal(arg, "off")) {
1026 14 : gensec_features &=
1027 : ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL);
1028 :
1029 14 : signing_state = SMB_SIGNING_OFF;
1030 14 : encryption_state = SMB_ENCRYPTION_OFF;
1031 459 : } else if (strequal(arg, "sign")) {
1032 324 : gensec_features |= GENSEC_FEATURE_SIGN;
1033 :
1034 324 : signing_state = SMB_SIGNING_REQUIRED;
1035 324 : encryption_state = SMB_ENCRYPTION_OFF;
1036 135 : } else if (strequal(arg, "encrypt")) {
1037 135 : gensec_features |= GENSEC_FEATURE_SEAL;
1038 :
1039 135 : signing_state = SMB_SIGNING_REQUIRED;
1040 135 : encryption_state = SMB_ENCRYPTION_REQUIRED;
1041 : } else {
1042 0 : fprintf(stderr,
1043 : "Failed to parse --client-protection\n");
1044 0 : exit(1);
1045 : }
1046 :
1047 473 : ok = cli_credentials_set_gensec_features(creds,
1048 : gensec_features,
1049 : CRED_SPECIFIED);
1050 473 : if (!ok) {
1051 0 : fprintf(stderr,
1052 : "Failed to set gensec feature!\n");
1053 0 : exit(1);
1054 : }
1055 :
1056 473 : ok = cli_credentials_set_smb_signing(creds,
1057 : signing_state,
1058 : CRED_SPECIFIED);
1059 473 : if (!ok) {
1060 0 : fprintf(stderr,
1061 : "Failed to set smb signing!\n");
1062 0 : exit(1);
1063 : }
1064 :
1065 473 : ok = cli_credentials_set_smb_encryption(creds,
1066 : encryption_state,
1067 : CRED_SPECIFIED);
1068 473 : if (!ok) {
1069 0 : fprintf(stderr,
1070 : "Failed to set smb encryption!\n");
1071 0 : exit(1);
1072 : }
1073 473 : break;
1074 : }
1075 : } /* switch */
1076 : }
1077 :
1078 : static struct poptOption popt_common_credentials[] = {
1079 : {
1080 : .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
1081 : .arg = (void *)popt_common_credentials_callback,
1082 : },
1083 : {
1084 : .longName = "user",
1085 : .shortName = 'U',
1086 : .argInfo = POPT_ARG_STRING,
1087 : .val = 'U',
1088 : .descrip = "Set the network username",
1089 : .argDescrip = "[DOMAIN/]USERNAME[%PASSWORD]",
1090 : },
1091 : {
1092 : .longName = "no-pass",
1093 : .shortName = 'N',
1094 : .argInfo = POPT_ARG_NONE,
1095 : .val = 'N',
1096 : .descrip = "Don't ask for a password",
1097 : },
1098 : {
1099 : .longName = "password",
1100 : .argInfo = POPT_ARG_STRING,
1101 : .val = OPT_PASSWORD,
1102 : .descrip = "Password",
1103 : },
1104 : {
1105 : .longName = "pw-nt-hash",
1106 : .argInfo = POPT_ARG_NONE,
1107 : .val = OPT_NT_HASH,
1108 : .descrip = "The supplied password is the NT hash",
1109 : },
1110 : {
1111 : .longName = "authentication-file",
1112 : .shortName = 'A',
1113 : .argInfo = POPT_ARG_STRING,
1114 : .val = 'A',
1115 : .descrip = "Get the credentials from a file",
1116 : .argDescrip = "FILE",
1117 : },
1118 : {
1119 : .longName = "machine-pass",
1120 : .shortName = 'P',
1121 : .argInfo = POPT_ARG_NONE,
1122 : .val = 'P',
1123 : .descrip = "Use stored machine account password",
1124 : },
1125 : {
1126 : .longName = "simple-bind-dn",
1127 : .argInfo = POPT_ARG_STRING,
1128 : .val = OPT_SIMPLE_BIND_DN,
1129 : .descrip = "DN to use for a simple bind",
1130 : .argDescrip = "DN",
1131 : },
1132 : {
1133 : .longName = "use-kerberos",
1134 : .argInfo = POPT_ARG_STRING,
1135 : .val = OPT_USE_KERBEROS,
1136 : .descrip = "Use Kerberos authentication",
1137 : .argDescrip = "desired|required|off",
1138 : },
1139 : {
1140 : .longName = "use-krb5-ccache",
1141 : .argInfo = POPT_ARG_STRING,
1142 : .val = OPT_USE_KERBEROS_CCACHE,
1143 : .descrip = "Credentials cache location for Kerberos",
1144 : .argDescrip = "CCACHE",
1145 : },
1146 : {
1147 : .longName = "use-winbind-ccache",
1148 : .argInfo = POPT_ARG_NONE,
1149 : .val = OPT_USE_WINBIND_CCACHE,
1150 : .descrip = "Use the winbind ccache for authentication",
1151 : },
1152 : {
1153 : .longName = "client-protection",
1154 : .argInfo = POPT_ARG_STRING,
1155 : .val = OPT_CLIENT_PROTECTION,
1156 : .descrip = "Configure used protection for client connections",
1157 : .argDescrip = "sign|encrypt|off",
1158 : },
1159 : POPT_TABLEEND
1160 : };
1161 :
1162 : /**********************************************************
1163 : * VERSION POPT
1164 : **********************************************************/
1165 :
1166 0 : static void popt_version_callback(poptContext ctx,
1167 : enum poptCallbackReason reason,
1168 : const struct poptOption *opt,
1169 : const char *arg,
1170 : const void *data)
1171 : {
1172 0 : switch(opt->val) {
1173 0 : case 'V':
1174 0 : printf("Version %s\n", SAMBA_VERSION_STRING);
1175 0 : exit(0);
1176 : }
1177 0 : }
1178 :
1179 : static struct poptOption popt_common_version[] = {
1180 : {
1181 : .argInfo = POPT_ARG_CALLBACK,
1182 : .arg = (void *)popt_version_callback,
1183 : },
1184 : {
1185 : .longName = "version",
1186 : .shortName = 'V',
1187 : .argInfo = POPT_ARG_NONE,
1188 : .val = 'V',
1189 : .descrip = "Print version",
1190 : },
1191 : POPT_TABLEEND
1192 : };
1193 :
1194 : /**********************************************************
1195 : * DAEMON POPT
1196 : **********************************************************/
1197 :
1198 232 : static void popt_daemon_callback(poptContext ctx,
1199 : enum poptCallbackReason reason,
1200 : const struct poptOption *opt,
1201 : const char *arg,
1202 : const void *data)
1203 : {
1204 232 : switch(opt->val) {
1205 0 : case OPT_DAEMON:
1206 0 : cmdline_daemon_cfg.daemon = true;
1207 0 : break;
1208 55 : case OPT_INTERACTIVE:
1209 55 : cmdline_daemon_cfg.interactive = true;
1210 55 : cmdline_daemon_cfg.fork = false;
1211 55 : break;
1212 61 : case OPT_FORK:
1213 61 : cmdline_daemon_cfg.fork = false;
1214 61 : break;
1215 116 : case OPT_NO_PROCESS_GROUP:
1216 116 : cmdline_daemon_cfg.no_process_group = true;
1217 116 : break;
1218 : }
1219 232 : }
1220 :
1221 : static struct poptOption popt_common_daemon[] = {
1222 : {
1223 : .argInfo = POPT_ARG_CALLBACK,
1224 : .arg = (void *)popt_daemon_callback
1225 : },
1226 : {
1227 : .longName = "daemon",
1228 : .shortName = 'D',
1229 : .argInfo = POPT_ARG_NONE,
1230 : .arg = NULL,
1231 : .val = OPT_DAEMON,
1232 : .descrip = "Become a daemon (default)" ,
1233 : },
1234 : {
1235 : .longName = "interactive",
1236 : .shortName = 'i',
1237 : .argInfo = POPT_ARG_NONE,
1238 : .arg = NULL,
1239 : .val = OPT_INTERACTIVE,
1240 : .descrip = "Run interactive (not a daemon) and log to stdout",
1241 : },
1242 : {
1243 : .longName = "foreground",
1244 : .shortName = 'F',
1245 : .argInfo = POPT_ARG_NONE,
1246 : .arg = NULL,
1247 : .val = OPT_FORK,
1248 : .descrip = "Run daemon in foreground (for daemontools, etc.)",
1249 : },
1250 : {
1251 : .longName = "no-process-group",
1252 : .shortName = '\0',
1253 : .argInfo = POPT_ARG_NONE,
1254 : .arg = NULL,
1255 : .val = OPT_NO_PROCESS_GROUP,
1256 : .descrip = "Don't create a new process group" ,
1257 : },
1258 : POPT_TABLEEND
1259 : };
1260 :
1261 : /**********************************************************
1262 : * LEGACY S3 POPT
1263 : **********************************************************/
1264 :
1265 9 : static void popt_legacy_s3_callback(poptContext ctx,
1266 : enum poptCallbackReason reason,
1267 : const struct poptOption *opt,
1268 : const char *arg,
1269 : const void *data)
1270 : {
1271 9 : struct cli_credentials *creds = samba_cmdline_get_creds();
1272 : bool ok;
1273 :
1274 9 : switch(opt->val) {
1275 9 : case 'k':
1276 9 : fprintf(stderr,
1277 : "WARNING: The option -k|--kerberos is deprecated!\n");
1278 :
1279 9 : ok = cli_credentials_set_kerberos_state(creds,
1280 : CRED_USE_KERBEROS_REQUIRED,
1281 : CRED_SPECIFIED);
1282 9 : if (!ok) {
1283 0 : fprintf(stderr,
1284 : "Failed to set Kerberos state to %s!\n", arg);
1285 0 : exit(1);
1286 : }
1287 :
1288 9 : skip_password_callback = true;
1289 9 : break;
1290 : }
1291 9 : }
1292 :
1293 : /* We allow '-k yes' too. */
1294 : static struct poptOption popt_legacy_s3[] = {
1295 : {
1296 : .argInfo = POPT_ARG_CALLBACK,
1297 : .arg = (void *)popt_legacy_s3_callback,
1298 : },
1299 : {
1300 : .longName = "kerberos",
1301 : .shortName = 'k',
1302 : .argInfo = POPT_ARG_NONE,
1303 : .val = 'k',
1304 : .descrip = "DEPRECATED: Migrate to --use-kerberos",
1305 : },
1306 : POPT_TABLEEND
1307 : };
1308 :
1309 : /**********************************************************
1310 : * LEGACY S4 POPT
1311 : **********************************************************/
1312 :
1313 679 : static void popt_legacy_s4_callback(poptContext ctx,
1314 : enum poptCallbackReason reason,
1315 : const struct poptOption *opt,
1316 : const char *arg,
1317 : const void *data)
1318 : {
1319 679 : struct cli_credentials *creds = samba_cmdline_get_creds();
1320 : bool ok;
1321 :
1322 679 : switch(opt->val) {
1323 679 : case 'k': {
1324 679 : enum credentials_use_kerberos use_kerberos =
1325 : CRED_USE_KERBEROS_REQUIRED;
1326 :
1327 679 : fprintf(stderr,
1328 : "WARNING: The option -k|--kerberos is deprecated!\n");
1329 :
1330 679 : if (arg != NULL) {
1331 679 : if (strcasecmp_m(arg, "yes") == 0) {
1332 511 : use_kerberos = CRED_USE_KERBEROS_REQUIRED;
1333 168 : } else if (strcasecmp_m(arg, "no") == 0) {
1334 168 : use_kerberos = CRED_USE_KERBEROS_DISABLED;
1335 : } else {
1336 0 : fprintf(stderr,
1337 : "Error parsing -k %s. Should be "
1338 : "-k [yes|no]\n",
1339 : arg);
1340 0 : exit(1);
1341 : }
1342 : }
1343 :
1344 679 : ok = cli_credentials_set_kerberos_state(creds,
1345 : use_kerberos,
1346 : CRED_SPECIFIED);
1347 679 : if (!ok) {
1348 0 : fprintf(stderr,
1349 : "Failed to set Kerberos state to %s!\n", arg);
1350 0 : exit(1);
1351 : }
1352 :
1353 679 : break;
1354 : }
1355 : }
1356 679 : }
1357 :
1358 : static struct poptOption popt_legacy_s4[] = {
1359 : {
1360 : .argInfo = POPT_ARG_CALLBACK,
1361 : .arg = (void *)popt_legacy_s4_callback,
1362 : },
1363 : {
1364 : .longName = "kerberos",
1365 : .shortName = 'k',
1366 : .argInfo = POPT_ARG_STRING,
1367 : .val = 'k',
1368 : .descrip = "DEPRECATED: Migrate to --use-kerberos",
1369 : },
1370 : POPT_TABLEEND
1371 : };
1372 :
1373 33429 : struct poptOption *samba_cmdline_get_popt(enum smb_cmdline_popt_options opt)
1374 : {
1375 33429 : switch (opt) {
1376 2020 : case SAMBA_CMDLINE_POPT_OPT_DEBUG_ONLY:
1377 2020 : return popt_common_debug;
1378 : break;
1379 2020 : case SAMBA_CMDLINE_POPT_OPT_OPTION_ONLY:
1380 2020 : return popt_common_option;
1381 : break;
1382 137 : case SAMBA_CMDLINE_POPT_OPT_CONFIG_ONLY:
1383 137 : return popt_common_config;
1384 : break;
1385 4910 : case SAMBA_CMDLINE_POPT_OPT_SAMBA:
1386 4910 : return popt_common_samba;
1387 : break;
1388 4790 : case SAMBA_CMDLINE_POPT_OPT_CONNECTION:
1389 4790 : return popt_common_connection;
1390 : break;
1391 4612 : case SAMBA_CMDLINE_POPT_OPT_CREDENTIALS:
1392 4612 : return popt_common_credentials;
1393 : break;
1394 8923 : case SAMBA_CMDLINE_POPT_OPT_VERSION:
1395 8923 : return popt_common_version;
1396 : break;
1397 155 : case SAMBA_CMDLINE_POPT_OPT_DAEMON:
1398 155 : return popt_common_daemon;
1399 : break;
1400 1250 : case SAMBA_CMDLINE_POPT_OPT_SAMBA_LDB:
1401 1250 : return popt_common_samba_ldb;
1402 : break;
1403 2257 : case SAMBA_CMDLINE_POPT_OPT_LEGACY_S3:
1404 2257 : return popt_legacy_s3;
1405 : break;
1406 2355 : case SAMBA_CMDLINE_POPT_OPT_LEGACY_S4:
1407 2355 : return popt_legacy_s4;
1408 : break;
1409 : }
1410 :
1411 : /* Never reached */
1412 0 : return NULL;
1413 : }
|