r2155: Reformat, plus steal from Samba4 :-).
[tprouty/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
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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 pstring user_socket_options;
37 extern BOOL AllowDebugChange;
38 extern BOOL override_logfile;
39
40 struct user_auth_info cmdline_auth_info;
41
42 static void popt_common_callback(poptContext con, 
43                            enum poptCallbackReason reason,
44                            const struct poptOption *opt,
45                            const char *arg, const void *data)
46 {
47         pstring logfile;
48         const char *pname;
49         
50         /* Find out basename of current program */
51         pname = strrchr_m(poptGetInvocationName(con),'/');
52
53         if (!pname)
54                 pname = poptGetInvocationName(con);
55         else 
56                 pname++;
57
58         if (reason == POPT_CALLBACK_REASON_PRE) {
59                 pstr_sprintf(logfile, "%s/log.%s", dyn_LOGFILEBASE, pname);
60                 lp_set_logfile(logfile);
61                 return;
62         }
63
64         switch(opt->val) {
65         case 'd':
66                 if (arg) {
67                         debug_parse_levels(arg);
68                         AllowDebugChange = False;
69                 }
70                 break;
71
72         case 'V':
73                 printf( "Version %s\n", SAMBA_VERSION_STRING);
74                 exit(0);
75                 break;
76
77         case 'O':
78                 if (arg) {
79                         pstrcpy(user_socket_options,arg);
80                 }
81                 break;
82
83         case 's':
84                 if (arg) {
85                         pstrcpy(dyn_CONFIGFILE, arg);
86                 }
87                 break;
88
89         case 'n':
90                 if (arg) {
91                         set_global_myname(arg);
92                 }
93                 break;
94
95         case 'l':
96                 if (arg) {
97                         pstr_sprintf(logfile, "%s/log.%s", arg, pname);
98                         lp_set_logfile(logfile);
99                         override_logfile = True;
100                 }
101                 break;
102
103         case 'i':
104                 if (arg) {
105                           set_global_scope(arg);
106                 }
107                 break;
108
109         case 'W':
110                 if (arg) {
111                         set_global_myworkgroup(arg);
112                 }
113                 break;
114         }
115 }
116
117 struct poptOption popt_common_connection[] = {
118         { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback },
119         { "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use",
120           "SOCKETOPTIONS" },
121         { "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" },
122         { "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" },
123         { "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" },
124
125         POPT_TABLEEND
126 };
127
128 struct poptOption popt_common_samba[] = {
129         { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_callback },
130         { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
131         { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternative configuration file", "CONFIGFILE" },
132         { "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Basename for log/debug files", "LOGFILEBASE" },
133         { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
134         POPT_TABLEEND
135 };
136
137 struct poptOption popt_common_version[] = {
138         { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback },
139         { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
140         POPT_TABLEEND
141 };
142
143
144
145 /****************************************************************************
146  * get a password from a a file or file descriptor
147  * exit on failure
148  * ****************************************************************************/
149 static void get_password_file(struct user_auth_info *a)
150 {
151         int fd = -1;
152         char *p;
153         BOOL close_it = False;
154         pstring spec;
155         char pass[128];
156
157         if ((p = getenv("PASSWD_FD")) != NULL) {
158                 pstrcpy(spec, "descriptor ");
159                 pstrcat(spec, p);
160                 sscanf(p, "%d", &fd);
161                 close_it = False;
162         } else if ((p = getenv("PASSWD_FILE")) != NULL) {
163                 fd = sys_open(p, O_RDONLY, 0);
164                 pstrcpy(spec, p);
165                 if (fd < 0) {
166                         fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
167                                         spec, strerror(errno));
168                         exit(1);
169                 }
170                 close_it = True;
171         }
172
173         for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
174                 p && p - pass < sizeof(pass);) {
175                 switch (read(fd, p, 1)) {
176                 case 1:
177                         if (*p != '\n' && *p != '\0') {
178                                 *++p = '\0'; /* advance p, and null-terminate pass */
179                                 break;
180                         }
181                 case 0:
182                         if (p - pass) {
183                                 *p = '\0'; /* null-terminate it, just in case... */
184                                 p = NULL; /* then force the loop condition to become false */
185                                 break;
186                         } else {
187                                 fprintf(stderr, "Error reading password from file %s: %s\n",
188                                                 spec, "empty password\n");
189                                 exit(1);
190                         }
191
192                 default:
193                         fprintf(stderr, "Error reading password from file %s: %s\n",
194                                         spec, strerror(errno));
195                         exit(1);
196                 }
197         }
198         pstrcpy(a->password, pass);
199         if (close_it)
200                 close(fd);
201 }
202
203 static void get_credentials_file(const char *file, struct user_auth_info *info) 
204 {
205         XFILE *auth;
206         fstring buf;
207         uint16 len = 0;
208         char *ptr, *val, *param;
209
210         if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
211         {
212                 /* fail if we can't open the credentials file */
213                 d_printf("ERROR: Unable to open credentials file!\n");
214                 exit(-1);
215         }
216
217         while (!x_feof(auth))
218         {
219                 /* get a line from the file */
220                 if (!x_fgets(buf, sizeof(buf), auth))
221                         continue;
222                 len = strlen(buf);
223
224                 if ((len) && (buf[len-1]=='\n'))
225                 {
226                         buf[len-1] = '\0';
227                         len--;
228                 }
229                 if (len == 0)
230                         continue;
231
232                 /* break up the line into parameter & value.
233                  * will need to eat a little whitespace possibly */
234                 param = buf;
235                 if (!(ptr = strchr_m (buf, '=')))
236                         continue;
237
238                 val = ptr+1;
239                 *ptr = '\0';
240
241                 /* eat leading white space */
242                 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
243                         val++;
244
245                 if (strwicmp("password", param) == 0)
246                 {
247                         pstrcpy(info->password, val);
248                         info->got_pass = True;
249                 }
250                 else if (strwicmp("username", param) == 0)
251                         pstrcpy(info->username, val);
252                 else if (strwicmp("domain", param) == 0)
253                         set_global_myworkgroup(val);
254                 memset(buf, 0, sizeof(buf));
255         }
256         x_fclose(auth);
257 }
258
259 /* Handle command line options:
260  *              -U,--user
261  *              -A,--authentication-file
262  *              -k,--use-kerberos
263  *              -N,--no-pass
264  *              -S,--signing
265  *              -P --machine-pass
266  */
267
268
269 static void popt_common_credentials_callback(poptContext con, 
270                                         enum poptCallbackReason reason,
271                                         const struct poptOption *opt,
272                                         const char *arg, const void *data)
273 {
274         char *p;
275
276         if (reason == POPT_CALLBACK_REASON_PRE) {
277                 cmdline_auth_info.use_kerberos = False;
278                 cmdline_auth_info.got_pass = False;
279                 cmdline_auth_info.signing_state = Undefined;
280                 pstrcpy(cmdline_auth_info.username, "GUEST");   
281
282                 if (getenv("LOGNAME"))pstrcpy(cmdline_auth_info.username,getenv("LOGNAME"));
283
284                 if (getenv("USER")) {
285                         pstrcpy(cmdline_auth_info.username,getenv("USER"));
286
287                         if ((p = strchr_m(cmdline_auth_info.username,'%'))) {
288                                 *p = 0;
289                                 pstrcpy(cmdline_auth_info.password,p+1);
290                                 cmdline_auth_info.got_pass = True;
291                                 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(cmdline_auth_info.password));
292                         }
293                 }
294
295                 if (getenv("PASSWD")) {
296                         pstrcpy(cmdline_auth_info.password,getenv("PASSWD"));
297                         cmdline_auth_info.got_pass = True;
298                 }
299
300                 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
301                         get_password_file(&cmdline_auth_info);
302                         cmdline_auth_info.got_pass = True;
303                 }
304
305                 return;
306         }
307
308         switch(opt->val) {
309         case 'U':
310                 {
311                         char *lp;
312
313                         pstrcpy(cmdline_auth_info.username,arg);
314                         if ((lp=strchr_m(cmdline_auth_info.username,'%'))) {
315                                 *lp = 0;
316                                 pstrcpy(cmdline_auth_info.password,lp+1);
317                                 cmdline_auth_info.got_pass = True;
318                                 memset(strchr_m(arg,'%')+1,'X',strlen(cmdline_auth_info.password));
319                         }
320                 }
321                 break;
322
323         case 'A':
324                 get_credentials_file(arg, &cmdline_auth_info);
325                 break;
326
327         case 'k':
328 #ifndef HAVE_KRB5
329                 d_printf("No kerberos support compiled in\n");
330                 exit(1);
331 #else
332                 cmdline_auth_info.use_kerberos = True;
333                 cmdline_auth_info.got_pass = True;
334 #endif
335                 break;
336
337         case 'S':
338                 {
339                         cmdline_auth_info.signing_state = -1;
340                         if (strequal(arg, "off") || strequal(arg, "no") || strequal(arg, "false"))
341                                 cmdline_auth_info.signing_state = False;
342                         else if (strequal(arg, "on") || strequal(arg, "yes") || strequal(arg, "true") ||
343                                         strequal(arg, "auto") )
344                                 cmdline_auth_info.signing_state = True;
345                         else if (strequal(arg, "force") || strequal(arg, "required") || strequal(arg, "forced"))
346                                 cmdline_auth_info.signing_state = Required;
347                         else {
348                                 fprintf(stderr, "Unknown signing option %s\n", arg );
349                                 exit(1);
350                         }
351                 }
352                 break;
353         case 'P':
354                 {
355                         char *opt_password = NULL;
356                         /* it is very useful to be able to make ads queries as the
357                            machine account for testing purposes and for domain leave */
358                         
359                         if (!secrets_init()) {
360                                 d_printf("ERROR: Unable to open secrets database\n");
361                                 exit(1);
362                         }
363                         
364                         opt_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
365                         
366                         if (!opt_password) {
367                                 d_printf("ERROR: Unable to fetch machine password\n");
368                                 exit(1);
369                         }
370                         pstr_sprintf(cmdline_auth_info.username, "%s$", 
371                                      global_myname());
372                         pstrcpy(cmdline_auth_info.password,opt_password);
373                         SAFE_FREE(opt_password);
374
375                         /* machine accounts only work with kerberos */
376                         cmdline_auth_info.use_kerberos = True;
377                         cmdline_auth_info.got_pass = True;
378                 }
379                 break;
380         }
381 }
382
383
384
385 struct poptOption popt_common_credentials[] = {
386         { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_credentials_callback },
387         { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" },
388         { "no-pass", 'N', POPT_ARG_NONE, &cmdline_auth_info.got_pass, 0, "Don't ask for a password" },
389         { "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, 'k', "Use kerberos (active directory) authentication" },
390         { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" },
391         { "signing", 'S', POPT_ARG_STRING, NULL, 'S', "Set the client signing state", "on|off|required" },
392         {"machine-pass", 'P', POPT_ARG_NONE, NULL, 'P', "Use stored machine account password" },
393         POPT_TABLEEND
394 };