Remove pstrings from client/client.c by doing a large rewrite.
[kai/samba.git] / source / lib / popt_common.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Common popt routines
4
5    Copyright (C) Tim Potter 2001,2002
6    Copyright (C) Jelmer Vernooij 2002,2003
7    Copyright (C) James Peach 2006
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24
25 /* Handle command line options:
26  *              -d,--debuglevel 
27  *              -s,--configfile 
28  *              -O,--socket-options 
29  *              -V,--version
30  *              -l,--log-base
31  *              -n,--netbios-name
32  *              -W,--workgroup
33  *              -i,--scope
34  */
35
36 extern bool AllowDebugChange;
37 extern bool override_logfile;
38
39 static struct user_auth_info cmdline_auth_info;
40
41 const char *get_cmdline_auth_info_username(void)
42 {
43         if (!cmdline_auth_info.username) {
44                 return "";
45         }
46         return cmdline_auth_info.username;
47 }
48
49 void set_cmdline_auth_info_username(const char *username)
50 {
51         SAFE_FREE(cmdline_auth_info.username);
52         cmdline_auth_info.username = SMB_STRDUP(username);
53         if (!cmdline_auth_info.username) {
54                 exit(ENOMEM);
55         }
56 }
57
58 const char *get_cmdline_auth_info_password(void)
59 {
60         if (!cmdline_auth_info.password) {
61                 return "";
62         }
63         return cmdline_auth_info.password;
64 }
65
66 void set_cmdline_auth_info_password(const char *password)
67 {
68         SAFE_FREE(cmdline_auth_info.password);
69         cmdline_auth_info.password = SMB_STRDUP(password);
70         if (!cmdline_auth_info.password) {
71                 exit(ENOMEM);
72         }
73         cmdline_auth_info.got_pass = true;
74 }
75
76 int get_cmdline_auth_info_signing_state(void)
77 {
78         return cmdline_auth_info.signing_state;
79 }
80
81 bool get_cmdline_auth_info_use_kerberos(void)
82 {
83         return cmdline_auth_info.use_kerberos;
84 }
85
86 bool get_cmdline_auth_info_got_pass(void)
87 {
88         return cmdline_auth_info.got_pass;
89 }
90
91 static void set_logfile(poptContext con, const char * arg)
92 {
93
94         char *logfile = NULL;
95         const char *pname;
96
97         /* Find out basename of current program */
98         pname = strrchr_m(poptGetInvocationName(con),'/');
99
100         if (!pname)
101                 pname = poptGetInvocationName(con);
102         else
103                 pname++;
104
105         if (asprintf(&logfile, "%s/log.%s", arg, pname) < 0) {
106                 return;
107         }
108         lp_set_logfile(logfile);
109         SAFE_FREE(logfile);
110 }
111
112 static bool PrintSambaVersionString;
113
114 static void popt_common_callback(poptContext con,
115                            enum poptCallbackReason reason,
116                            const struct poptOption *opt,
117                            const char *arg, const void *data)
118 {
119
120         if (reason == POPT_CALLBACK_REASON_PRE) {
121                 set_logfile(con, dyn_LOGFILEBASE);
122                 return;
123         }
124
125         if (reason == POPT_CALLBACK_REASON_POST) {
126                 if (!PrintSambaVersionString) return;
127
128                 printf( "Version %s\n", SAMBA_VERSION_STRING);
129                 exit(0);
130                 return;
131         }
132
133         switch(opt->val) {
134         case 'd':
135                 if (arg) {
136                         debug_parse_levels(arg);
137                         AllowDebugChange = False;
138                 }
139                 break;
140
141         case 'V':
142                 PrintSambaVersionString = True;
143                 break;
144
145         case 'O':
146                 if (arg) {
147                         lp_do_parameter(-1, "socket options", arg);
148                 }
149                 break;
150
151         case 's':
152                 if (arg) {
153                         strlcpy(dyn_CONFIGFILE, arg,sizeof(dyn_CONFIGFILE));
154                 }
155                 break;
156
157         case 'n':
158                 if (arg) {
159                         set_global_myname(arg);
160                 }
161                 break;
162
163         case 'l':
164                 if (arg) {
165                         set_logfile(con, arg);
166                         override_logfile = True;
167                         pstr_sprintf(dyn_LOGFILEBASE, "%s", arg);
168                 }
169                 break;
170
171         case 'i':
172                 if (arg) {
173                           set_global_scope(arg);
174                 }
175                 break;
176
177         case 'W':
178                 if (arg) {
179                         set_global_myworkgroup(arg);
180                 }
181                 break;
182         }
183 }
184
185 struct poptOption popt_common_connection[] = {
186         { NULL, 0, POPT_ARG_CALLBACK, (void *)popt_common_callback },
187         { "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use",
188           "SOCKETOPTIONS" },
189         { "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" },
190         { "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" },
191         { "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" },
192
193         POPT_TABLEEND
194 };
195
196 struct poptOption popt_common_samba[] = {
197         { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, (void *)popt_common_callback },
198         { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
199         { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternate configuration file", "CONFIGFILE" },
200         { "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Base name for log files", "LOGFILEBASE" },
201         { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
202         POPT_TABLEEND
203 };
204
205 struct poptOption popt_common_version[] = {
206         { NULL, 0, POPT_ARG_CALLBACK, (void *)popt_common_callback },
207         { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
208         POPT_TABLEEND
209 };
210
211
212 /* Handle command line options:
213  *              --sbindir
214  *              --bindir
215  *              --swatdir
216  *              --lmhostsfile
217  *              --libdir
218  *              --shlibext
219  *              --lockdir
220  *              --piddir
221  *              --smb-passwd-file
222  *              --private-dir
223  */
224
225 enum dyn_item{
226         DYN_SBINDIR = 1,
227         DYN_BINDIR,
228         DYN_SWATDIR,
229         DYN_LMHOSTSFILE,
230         DYN_LIBDIR,
231         DYN_SHLIBEXT,
232         DYN_LOCKDIR,
233         DYN_PIDDIR,
234         DYN_SMB_PASSWD_FILE,
235         DYN_PRIVATE_DIR,
236 };
237
238
239 static void popt_dynconfig_callback(poptContext con,
240                            enum poptCallbackReason reason,
241                            const struct poptOption *opt,
242                            const char *arg, const void *data)
243 {
244
245         switch (opt->val) {
246         case DYN_SBINDIR:
247                 if (arg) {
248                         dyn_SBINDIR = SMB_STRDUP(arg);
249                 }
250                 break;
251
252         case DYN_BINDIR:
253                 if (arg) {
254                         dyn_BINDIR = SMB_STRDUP(arg);
255                 }
256                 break;
257
258         case DYN_SWATDIR:
259                 if (arg) {
260                         dyn_SWATDIR = SMB_STRDUP(arg);
261                 }
262                 break;
263
264         case DYN_LMHOSTSFILE:
265                 if (arg) {
266                         strlcpy(dyn_LMHOSTSFILE, arg,sizeof(dyn_LMHOSTSFILE));
267                 }
268                 break;
269
270         case DYN_LIBDIR:
271                 if (arg) {
272                         strlcpy(dyn_LIBDIR, arg,sizeof(dyn_LIBDIR));
273                 }
274                 break;
275
276         case DYN_SHLIBEXT:
277                 if (arg) {
278                         fstrcpy(dyn_SHLIBEXT, arg);
279                 }
280                 break;
281
282         case DYN_LOCKDIR:
283                 if (arg) {
284                         strlcpy(dyn_LOCKDIR, arg,sizeof(dyn_LOCKDIR));
285                 }
286                 break;
287
288         case DYN_PIDDIR:
289                 if (arg) {
290                         strlcpy(dyn_PIDDIR, arg,sizeof(dyn_PIDDIR));
291                 }
292                 break;
293
294         case DYN_SMB_PASSWD_FILE:
295                 if (arg) {
296                         strlcpy(dyn_SMB_PASSWD_FILE, arg,sizeof(dyn_SMB_PASSWD_FILE));
297                 }
298                 break;
299
300         case DYN_PRIVATE_DIR:
301                 if (arg) {
302                         strlcpy(dyn_PRIVATE_DIR, arg, sizeof(dyn_PRIVATE_DIR));
303                 }
304                 break;
305
306         }
307 }
308
309 const struct poptOption popt_common_dynconfig[] = {
310
311         { NULL, '\0', POPT_ARG_CALLBACK, (void *)popt_dynconfig_callback },
312
313         { "sbindir", '\0' , POPT_ARG_STRING, NULL, DYN_SBINDIR,
314             "Path to sbin directory", "SBINDIR" },
315         { "bindir", '\0' , POPT_ARG_STRING, NULL, DYN_BINDIR,
316             "Path to bin directory", "BINDIR" },
317         { "swatdir", '\0' , POPT_ARG_STRING, NULL, DYN_SWATDIR,
318             "Path to SWAT installation directory", "SWATDIR" },
319         { "lmhostsfile", '\0' , POPT_ARG_STRING, NULL, DYN_LMHOSTSFILE,
320             "Path to lmhosts file", "LMHOSTSFILE" },
321         { "libdir", '\0' , POPT_ARG_STRING, NULL, DYN_LIBDIR,
322             "Path to shared library directory", "LIBDIR" },
323         { "shlibext", '\0' , POPT_ARG_STRING, NULL, DYN_SHLIBEXT,
324             "Shared library extension", "SHLIBEXT" },
325         { "lockdir", '\0' , POPT_ARG_STRING, NULL, DYN_LOCKDIR,
326             "Path to lock file directory", "LOCKDIR" },
327         { "piddir", '\0' , POPT_ARG_STRING, NULL, DYN_PIDDIR,
328             "Path to PID file directory", "PIDDIR" },
329         { "smb-passwd-file", '\0' , POPT_ARG_STRING, NULL, DYN_SMB_PASSWD_FILE,
330             "Path to smbpasswd file", "SMB_PASSWD_FILE" },
331         { "private-dir", '\0' , POPT_ARG_STRING, NULL, DYN_PRIVATE_DIR,
332             "Path to private data directory", "PRIVATE_DIR" },
333
334         POPT_TABLEEND
335 };
336
337 /****************************************************************************
338  * get a password from a a file or file descriptor
339  * exit on failure
340  * ****************************************************************************/
341
342 static void get_password_file(void)
343 {
344         int fd = -1;
345         char *p;
346         bool close_it = False;
347         char *spec = NULL;
348         char pass[128];
349
350         if ((p = getenv("PASSWD_FD")) != NULL) {
351                 if (asprintf(&spec, "descriptor %s", p) < 0) {
352                         return;
353                 }
354                 sscanf(p, "%d", &fd);
355                 close_it = false;
356         } else if ((p = getenv("PASSWD_FILE")) != NULL) {
357                 fd = sys_open(p, O_RDONLY, 0);
358                 spec = SMB_STRDUP(p);
359                 if (fd < 0) {
360                         fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
361                                         spec, strerror(errno));
362                         exit(1);
363                 }
364                 close_it = True;
365         }
366
367         for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
368                 p && p - pass < sizeof(pass);) {
369                 switch (read(fd, p, 1)) {
370                 case 1:
371                         if (*p != '\n' && *p != '\0') {
372                                 *++p = '\0'; /* advance p, and null-terminate pass */
373                                 break;
374                         }
375                 case 0:
376                         if (p - pass) {
377                                 *p = '\0'; /* null-terminate it, just in case... */
378                                 p = NULL; /* then force the loop condition to become false */
379                                 break;
380                         } else {
381                                 fprintf(stderr, "Error reading password from file %s: %s\n",
382                                                 spec, "empty password\n");
383                                 SAFE_FREE(spec);
384                                 exit(1);
385                         }
386
387                 default:
388                         fprintf(stderr, "Error reading password from file %s: %s\n",
389                                         spec, strerror(errno));
390                         SAFE_FREE(spec);
391                         exit(1);
392                 }
393         }
394         SAFE_FREE(spec);
395
396         set_cmdline_auth_info_password(pass);
397         if (close_it) {
398                 close(fd);
399         }
400 }
401
402 static void get_credentials_file(const char *file, struct user_auth_info *info)
403 {
404         XFILE *auth;
405         fstring buf;
406         uint16 len = 0;
407         char *ptr, *val, *param;
408
409         if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
410         {
411                 /* fail if we can't open the credentials file */
412                 d_printf("ERROR: Unable to open credentials file!\n");
413                 exit(-1);
414         }
415
416         while (!x_feof(auth))
417         {
418                 /* get a line from the file */
419                 if (!x_fgets(buf, sizeof(buf), auth))
420                         continue;
421                 len = strlen(buf);
422
423                 if ((len) && (buf[len-1]=='\n'))
424                 {
425                         buf[len-1] = '\0';
426                         len--;
427                 }
428                 if (len == 0)
429                         continue;
430
431                 /* break up the line into parameter & value.
432                  * will need to eat a little whitespace possibly */
433                 param = buf;
434                 if (!(ptr = strchr_m (buf, '=')))
435                         continue;
436
437                 val = ptr+1;
438                 *ptr = '\0';
439
440                 /* eat leading white space */
441                 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
442                         val++;
443
444                 if (strwicmp("password", param) == 0) {
445                         SAFE_FREE(info->password);
446                         info->password = SMB_STRDUP(val);
447                         if (!info->password) {
448                                 exit(ENOMEM);
449                         }
450                         info->got_pass = True;
451                 } else if (strwicmp("username", param) == 0) {
452                         SAFE_FREE(info->username);
453                         info->username = SMB_STRDUP(val);
454                         if (!info->username) {
455                                 exit(ENOMEM);
456                         }
457                 } else if (strwicmp("domain", param) == 0) {
458                         set_global_myworkgroup(val);
459                 }
460                 memset(buf, 0, sizeof(buf));
461         }
462         x_fclose(auth);
463 }
464
465 /* Handle command line options:
466  *              -U,--user
467  *              -A,--authentication-file
468  *              -k,--use-kerberos
469  *              -N,--no-pass
470  *              -S,--signing
471  *              -P --machine-pass
472  */
473
474
475 static void popt_common_credentials_callback(poptContext con,
476                                         enum poptCallbackReason reason,
477                                         const struct poptOption *opt,
478                                         const char *arg, const void *data)
479 {
480         char *p;
481
482         if (reason == POPT_CALLBACK_REASON_PRE) {
483                 cmdline_auth_info.use_kerberos = False;
484                 cmdline_auth_info.got_pass = False;
485                 cmdline_auth_info.signing_state = Undefined;
486                 set_cmdline_auth_info_username("GUEST");
487
488                 if (getenv("LOGNAME")) {
489                         set_cmdline_auth_info_username(getenv("LOGNAME"));
490                 }
491
492                 if (getenv("USER")) {
493                         char *puser = SMB_STRDUP(getenv("USER"));
494                         if (!puser) {
495                                 exit(ENOMEM);
496                         }
497                         set_cmdline_auth_info_username(puser);
498
499                         if ((p = strchr_m(puser,'%'))) {
500                                 *p = 0;
501                                 set_cmdline_auth_info_password(p+1);
502                                 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(cmdline_auth_info.password));
503                         }
504                         SAFE_FREE(puser);
505                 }
506
507                 if (getenv("PASSWD")) {
508                         set_cmdline_auth_info_password(getenv("PASSWD"));
509                 }
510
511                 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
512                         get_password_file(&cmdline_auth_info);
513                         cmdline_auth_info.got_pass = True;
514                 }
515
516                 return;
517         }
518
519         switch(opt->val) {
520         case 'U':
521                 {
522                         char *lp;
523
524                         pstrcpy(cmdline_auth_info.username,arg);
525                         if ((lp=strchr_m(cmdline_auth_info.username,'%'))) {
526                                 *lp = 0;
527                                 pstrcpy(cmdline_auth_info.password,lp+1);
528                                 cmdline_auth_info.got_pass = True;
529                                 memset(strchr_m(arg,'%')+1,'X',strlen(cmdline_auth_info.password));
530                         }
531                 }
532                 break;
533
534         case 'A':
535                 get_credentials_file(arg);
536                 break;
537
538         case 'k':
539 #ifndef HAVE_KRB5
540                 d_printf("No kerberos support compiled in\n");
541                 exit(1);
542 #else
543                 cmdline_auth_info.use_kerberos = true;
544                 cmdline_auth_info.got_pass = true;
545 #endif
546                 break;
547
548         case 'S':
549                 {
550                         cmdline_auth_info.signing_state = -1;
551                         if (strequal(arg, "off") || strequal(arg, "no") || strequal(arg, "false")) {
552                                 cmdline_auth_info.signing_state = false;
553                         } else if (strequal(arg, "on") || strequal(arg, "yes") || strequal(arg, "true") ||
554                                         strequal(arg, "auto")) {
555                                 cmdline_auth_info.signing_state = true;
556                         } else if (strequal(arg, "force") || strequal(arg, "required") || strequal(arg, "forced")) {
557                                 cmdline_auth_info.signing_state = Required;
558                         } else {
559                                 fprintf(stderr, "Unknown signing option %s\n", arg );
560                                 exit(1);
561                         }
562                 }
563                 break;
564         case 'P':
565                 {
566                         char *opt_password = NULL;
567                         char *pwd = NULL;
568
569                         /* it is very useful to be able to make ads queries as the
570                            machine account for testing purposes and for domain leave */
571
572                         if (!secrets_init()) {
573                                 d_printf("ERROR: Unable to open secrets database\n");
574                                 exit(1);
575                         }
576
577                         opt_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
578
579                         if (!opt_password) {
580                                 d_printf("ERROR: Unable to fetch machine password\n");
581                                 exit(1);
582                         }
583                         if (asprintf(&pwd, "%s$", global_myname()) < 0) {
584                                 exit(ENOMEM);
585                         }
586                         set_cmdline_auth_info_username(pwd);
587                         set_cmdline_auth_info_password(opt_password);
588                         SAFE_FREE(pwd);
589                         SAFE_FREE(opt_password);
590
591                         /* machine accounts only work with kerberos */
592                         cmdline_auth_info.use_kerberos = true;
593                 }
594                 break;
595         }
596 }
597
598 struct poptOption popt_common_credentials[] = {
599         { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, (void *)popt_common_credentials_callback },
600         { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" },
601         { "no-pass", 'N', POPT_ARG_NONE, &cmdline_auth_info.got_pass, 0, "Don't ask for a password" },
602         { "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, 'k', "Use kerberos (active directory) authentication" },
603         { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" },
604         { "signing", 'S', POPT_ARG_STRING, NULL, 'S', "Set the client signing state", "on|off|required" },
605         {"machine-pass", 'P', POPT_ARG_NONE, NULL, 'P', "Use stored machine account password" },
606         POPT_TABLEEND
607 };