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