1/*
2   Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3   Copyright (C) 2003 Steve French  (sfrench@us.ibm.com)
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
18
19#ifndef _GNU_SOURCE
20#define _GNU_SOURCE
21#endif
22
23#include <stdlib.h>
24#include <stdio.h>
25#include <unistd.h>
26#include <pwd.h>
27#include <ctype.h>
28#include <sys/types.h>
29#include <sys/mount.h>
30#include <sys/stat.h>
31#include <sys/utsname.h>
32#include <sys/socket.h>
33#include <arpa/inet.h>
34#include <getopt.h>
35#include <errno.h>
36#include <netdb.h>
37#include <string.h>
38#include <mntent.h>
39#include <fcntl.h>
40
41#define MOUNT_CIFS_VERSION_MAJOR "1"
42#define MOUNT_CIFS_VERSION_MINOR "6"
43
44#ifndef MOUNT_CIFS_VENDOR_SUFFIX
45#define MOUNT_CIFS_VENDOR_SUFFIX ""
46#endif
47
48#ifndef MS_MOVE
49#define MS_MOVE 8192
50#endif
51
52char * thisprogram;
53int verboseflag = 0;
54static int got_password = 0;
55static int got_user = 0;
56static int got_domain = 0;
57static int got_ip = 0;
58static int got_unc = 0;
59static int got_uid = 0;
60static int got_gid = 0;
61static int free_share_name = 0;
62static char * user_name = NULL;
63char * mountpassword = NULL;
64
65
66/* BB finish BB
67
68        cifs_umount
69        open nofollow - avoid symlink exposure?
70        get owner of dir see if matches self or if root
71        call system(umount argv) etc.
72
73BB end finish BB */
74
75static void mount_cifs_usage(void)
76{
77	printf("\nUsage:  %s <remotetarget> <dir> -o <options>\n", thisprogram);
78	printf("\nMount the remote target, specified as a UNC name,");
79	printf(" to a local directory.\n\nOptions:\n");
80	printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
81	printf("\nLess commonly used options:");
82	printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,\n\trw,ro,sep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,directio");
83	printf("\n\nOptions not needed for servers supporting CIFS Unix extensions (e.g. most Samba versions):");
84	printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>");
85	printf("\n\nRarely used options:");
86	printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,dev,nodev");
87	printf("\n\nOptions are described in more detail in the manual page");
88	printf("\n\tman 8 mount.cifs\n");
89	printf("\nTo display the version number of the mount helper:");
90	printf("\n\t%s -V\n",thisprogram);
91
92	if(mountpassword) {
93		memset(mountpassword,0,64);
94		free(mountpassword);
95	}
96	exit(1);
97}
98
99/* caller frees username if necessary */
100static char * getusername(void) {
101	char *username = NULL;
102	struct passwd *password = getpwuid(getuid());
103
104	if (password) {
105		username = password->pw_name;
106	}
107	return username;
108}
109
110char * parse_cifs_url(char * unc_name)
111{
112	printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
113	return NULL;
114}
115
116static int open_cred_file(char * file_name)
117{
118	char * line_buf;
119	char * temp_val;
120	FILE * fs;
121	int i, length;
122	fs = fopen(file_name,"r");
123	if(fs == NULL)
124		return errno;
125	line_buf = malloc(4096);
126	if(line_buf == NULL)
127		return -ENOMEM;
128
129	while(fgets(line_buf,4096,fs)) {
130		/* parse line from credential file */
131
132		/* eat leading white space */
133		for(i=0;i<4086;i++) {
134			if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
135				break;
136			/* if whitespace - skip past it */
137		}
138		if (strncasecmp("username",line_buf+i,8) == 0) {
139			temp_val = strchr(line_buf + i,'=');
140			if(temp_val) {
141				/* go past equals sign */
142				temp_val++;
143				for(length = 0;length<4087;length++) {
144					if(temp_val[length] == '\n')
145						break;
146				}
147				if(length > 4086) {
148					printf("mount.cifs failed due to malformed username in credentials file");
149					memset(line_buf,0,4096);
150					if(mountpassword) {
151						memset(mountpassword,0,64);
152					}
153					exit(1);
154				} else {
155					got_user = 1;
156					user_name = calloc(1 + length,1);
157					/* BB adding free of user_name string before exit,
158						not really necessary but would be cleaner */
159					strncpy(user_name,temp_val, length);
160				}
161			}
162		} else if (strncasecmp("password",line_buf+i,8) == 0) {
163			temp_val = strchr(line_buf+i,'=');
164			if(temp_val) {
165				/* go past equals sign */
166				temp_val++;
167				for(length = 0;length<65;length++) {
168					if(temp_val[length] == '\n')
169						break;
170				}
171				if(length > 64) {
172					printf("mount.cifs failed: password in credentials file too long\n");
173					memset(line_buf,0, 4096);
174					if(mountpassword) {
175						memset(mountpassword,0,64);
176					}
177					exit(1);
178				} else {
179					if(mountpassword == NULL) {
180						mountpassword = calloc(65,1);
181					} else
182						memset(mountpassword,0,64);
183					if(mountpassword) {
184						/* BB add handling for commas in password here */
185						strncpy(mountpassword,temp_val,length);
186						got_password = 1;
187					}
188				}
189			}
190		}
191	}
192	fclose(fs);
193	if(line_buf) {
194		memset(line_buf,0,4096);
195		free(line_buf);
196	}
197	return 0;
198}
199
200static int get_password_from_file(int file_descript, char * filename)
201{
202	int rc = 0;
203	int i;
204	char c;
205
206	if(mountpassword == NULL)
207		mountpassword = calloc(65,1);
208	else
209		memset(mountpassword, 0, 64);
210
211	if(filename != NULL) {
212		file_descript = open(filename, O_RDONLY);
213		if(file_descript < 0) {
214			printf("mount.cifs failed. %s attempting to open password file %s\n",
215				   strerror(errno),filename);
216			exit(1);
217		}
218	}
219	/* else file already open and fd provided */
220
221	for(i=0;i<64;i++) {
222		rc = read(file_descript,&c,1);
223		if(rc < 0) {
224			printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
225			memset(mountpassword,0,64);
226			if(filename != NULL)
227				close(file_descript);
228			exit(1);
229		} else if(rc == 0) {
230			if(mountpassword[0] == 0) {
231				if(verboseflag)
232					printf("\nWarning: null password used since cifs password file empty");
233			}
234			break;
235		} else /* read valid character */ {
236			if((c == 0) || (c == '\n')) {
237				break;
238			} else
239				mountpassword[i] = c;
240		}
241	}
242	if((i == 64) && (verboseflag)) {
243		printf("\nWarning: password longer than 64 characters specified in cifs password file");
244	}
245	got_password = 1;
246	if(filename != NULL) {
247		close(file_descript);
248	}
249
250	return rc;
251}
252
253static int parse_options(char * options, int * filesys_flags)
254{
255	char * data;
256	char * percent_char = NULL;
257	char * value = NULL;
258	char * next_keyword = NULL;
259	int rc = 0;
260
261	if (!options)
262		return 1;
263	else
264		data = options;
265
266	if(verboseflag)
267		printf("\n parsing options: %s", options);
268
269/* while ((data = strsep(&options, ",")) != NULL) { */
270	while(data != NULL) {
271		/*  check if ends with trailing comma */
272		if(*data == 0)
273			break;
274
275		/* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
276		/* data  = next keyword */
277		/* value = next value ie stuff after equal sign */
278
279		next_keyword = strchr(data,',');
280
281		/* temporarily null terminate end of keyword=value pair */
282		if(next_keyword)
283			*next_keyword = 0;
284
285		/* if (!*data)
286			continue; */
287
288		/* temporarily null terminate keyword to make keyword and value distinct */
289		if ((value = strchr(data, '=')) != NULL) {
290			*value = '\0';
291			value++;
292		}
293
294		if (strncmp(data, "users",5) == 0) {
295			if(!value || !*value) {
296				strncpy(data,",,,,,",5);
297			}
298		} else if (strncmp(data, "user_xattr",10) == 0) {
299		   /* do nothing - need to skip so not parsed as user name */
300		} else if (strncmp(data, "user", 4) == 0) {
301			if (!value || !*value) {
302				if(data[4] == '\0') {
303					if(verboseflag)
304						printf("\nskipping empty user mount parameter\n");
305					/* remove the parm since it would otherwise be confusing
306					to the kernel code which would think it was a real username */
307						data[0] = ',';
308						data[1] = ',';
309						data[2] = ',';
310						data[3] = ',';
311					/* BB remove it from mount line so as not to confuse kernel code */
312				} else {
313					printf("username specified with no parameter\n");
314					return 1;	/* needs_arg; */
315				}
316			} else {
317				if (strnlen(value, 260) < 260) {
318					got_user=1;
319					percent_char = strchr(value,'%');
320					if(percent_char) {
321						*percent_char = ',';
322						if(mountpassword == NULL)
323							mountpassword = calloc(65,1);
324						if(mountpassword) {
325							if(got_password)
326								printf("\nmount.cifs warning - password specified twice\n");
327							got_password = 1;
328							percent_char++;
329							strncpy(mountpassword, percent_char,64);
330						/*  remove password from username */
331							while(*percent_char != 0) {
332								*percent_char = ',';
333								percent_char++;
334							}
335						}
336					}
337				} else {
338					printf("username too long\n");
339					return 1;
340				}
341			}
342		} else if (strncmp(data, "pass", 4) == 0) {
343			if (!value || !*value) {
344				if(got_password) {
345					printf("\npassword specified twice, ignoring second\n");
346				} else
347					got_password = 1;
348			} else if (strnlen(value, 17) < 17) {
349				if(got_password)
350					printf("\nmount.cifs warning - password specified twice\n");
351				got_password = 1;
352			} else {
353				printf("password too long\n");
354				return 1;
355			}
356		} else if (strncmp(data, "ip", 2) == 0) {
357			if (!value || !*value) {
358				printf("target ip address argument missing");
359			} else if (strnlen(value, 35) < 35) {
360				if(verboseflag)
361					printf("ip address %s override specified\n",value);
362				got_ip = 1;
363			} else {
364				printf("ip address too long\n");
365				return 1;
366			}
367		} else if ((strncmp(data, "unc", 3) == 0)
368		   || (strncmp(data, "target", 6) == 0)
369		   || (strncmp(data, "path", 4) == 0)) {
370			if (!value || !*value) {
371				printf("invalid path to network resource\n");
372				return 1;  /* needs_arg; */
373			} else if(strnlen(value,5) < 5) {
374				printf("UNC name too short");
375			}
376
377			if (strnlen(value, 300) < 300) {
378				got_unc = 1;
379				if (strncmp(value, "//", 2) == 0) {
380					if(got_unc)
381						printf("unc name specified twice, ignoring second\n");
382					else
383						got_unc = 1;
384				} else if (strncmp(value, "\\\\", 2) != 0) {
385					printf("UNC Path does not begin with // or \\\\ \n");
386					return 1;
387				} else {
388					if(got_unc)
389						printf("unc name specified twice, ignoring second\n");
390					else
391						got_unc = 1;
392				}
393			} else {
394				printf("CIFS: UNC name too long\n");
395				return 1;
396			}
397		} else if ((strncmp(data, "domain", 3) == 0)
398			   || (strncmp(data, "workgroup", 5) == 0)) {
399			if (!value || !*value) {
400				printf("CIFS: invalid domain name\n");
401				return 1;	/* needs_arg; */
402			}
403			if (strnlen(value, 65) < 65) {
404				got_domain = 1;
405			} else {
406				printf("domain name too long\n");
407				return 1;
408			}
409		} else if (strncmp(data, "cred", 4) == 0) {
410			if (value && *value) {
411				rc = open_cred_file(value);
412				if(rc) {
413					printf("error %d opening credential file %s\n",rc, value);
414					return 1;
415				}
416			} else {
417				printf("invalid credential file name specified\n");
418				return 1;
419			}
420		} else if (strncmp(data, "uid", 3) == 0) {
421			if (value && *value) {
422				got_uid = 1;
423			}
424		} else if (strncmp(data, "gid", 3) == 0) {
425			if (value && *value) {
426				got_gid = 1;
427			}
428       /* fmask and dmask synonyms for people used to smbfs syntax */
429		} else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
430			if (!value || !*value) {
431				printf ("Option '%s' requires a numerical argument\n", data);
432				return 1;
433			}
434
435			if (value[0] != '0') {
436				printf ("WARNING: '%s' not expressed in octal.\n", data);
437			}
438
439			if (strcmp (data, "fmask") == 0) {
440				printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
441				data = "file_mode"; /* BB fix this */
442			}
443		} else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
444			if (!value || !*value) {
445				printf ("Option '%s' requires a numerical argument\n", data);
446				return 1;
447			}
448
449			if (value[0] != '0') {
450				printf ("WARNING: '%s' not expressed in octal.\n", data);
451			}
452
453			if (strcmp (data, "dmask") == 0) {
454				printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
455				data = "dir_mode";
456			}
457			/* the following eight mount options should be
458			stripped out from what is passed into the kernel
459			since these eight options are best passed as the
460			mount flags rather than redundantly to the kernel
461			and could generate spurious warnings depending on the
462			level of the corresponding cifs vfs kernel code */
463		} else if (strncmp(data, "nosuid", 6) == 0) {
464			*filesys_flags |= MS_NOSUID;
465		} else if (strncmp(data, "suid", 4) == 0) {
466			*filesys_flags &= ~MS_NOSUID;
467		} else if (strncmp(data, "nodev", 5) == 0) {
468			*filesys_flags |= MS_NODEV;
469		} else if (strncmp(data, "dev", 3) == 0) {
470			*filesys_flags &= ~MS_NODEV;
471		} else if (strncmp(data, "noexec", 6) == 0) {
472			*filesys_flags |= MS_NOEXEC;
473		} else if (strncmp(data, "exec", 4) == 0) {
474			*filesys_flags &= ~MS_NOEXEC;
475		} else if (strncmp(data, "guest", 5) == 0) {
476			got_password=1;
477                        /* remove the parm since it would otherwise be logged by kern */
478 			data[0] = ',';
479                        data[1] = ',';
480                        data[2] = ',';
481 			data[3] = ',';
482			data[4] = ',';
483		} else if (strncmp(data, "ro", 2) == 0) {
484			*filesys_flags |= MS_RDONLY;
485		} else if (strncmp(data, "rw", 2) == 0) {
486			*filesys_flags &= ~MS_RDONLY;
487		} /* else if (strnicmp(data, "port", 4) == 0) {
488			if (value && *value) {
489				vol->port =
490					simple_strtoul(value, &value, 0);
491			}
492		} else if (strnicmp(data, "rsize", 5) == 0) {
493			if (value && *value) {
494				vol->rsize =
495					simple_strtoul(value, &value, 0);
496			}
497		} else if (strnicmp(data, "wsize", 5) == 0) {
498			if (value && *value) {
499				vol->wsize =
500					simple_strtoul(value, &value, 0);
501			}
502		} else if (strnicmp(data, "version", 3) == 0) {
503		} else {
504			printf("CIFS: Unknown mount option %s\n",data);
505		} */ /* nothing to do on those four mount options above.
506			Just pass to kernel and ignore them here */
507
508			/* move to next option */
509		data = next_keyword+1;
510
511		/* put overwritten equals sign back */
512		if(value) {
513			value--;
514			*value = '=';
515		}
516
517		/* put previous overwritten comma back */
518		if(next_keyword)
519			*next_keyword = ',';
520		else
521			data = NULL;
522	}
523	return 0;
524}
525
526/* Note that caller frees the returned buffer if necessary */
527char * parse_server(char ** punc_name)
528{
529	char * unc_name = *punc_name;
530	int length = strnlen(unc_name,1024);
531	char * share;
532	char * ipaddress_string = NULL;
533	struct hostent * host_entry;
534	struct in_addr server_ipaddr;
535	int rc;
536
537	if(length > 1023) {
538		printf("mount error: UNC name too long");
539		return NULL;
540	}
541	if (strncasecmp("cifs://",unc_name,7) == 0)
542		return parse_cifs_url(unc_name+7);
543	if (strncasecmp("smb://",unc_name,6) == 0) {
544		return parse_cifs_url(unc_name+6);
545	}
546
547	if(length < 3) {
548		/* BB add code to find DFS root here */
549		printf("\nMounting the DFS root for domain not implemented yet");
550		return NULL;
551	} else {
552		if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
553			/* check for nfs syntax ie server:share */
554			share = strchr(unc_name,':');
555			if(share) {
556				free_share_name = 1;
557				*punc_name = malloc(length+3);
558				*share = '/';
559				strncpy((*punc_name)+2,unc_name,length);
560				unc_name = *punc_name;
561				unc_name[length+2] = 0;
562				goto continue_unc_parsing;
563			} else {
564				printf("mount error: improperly formatted UNC name.");
565				printf(" %s does not begin with \\\\ or //\n",unc_name);
566				return NULL;
567			}
568		} else {
569continue_unc_parsing:
570			unc_name[0] = '/';
571			unc_name[1] = '/';
572			unc_name += 2;
573			if ((share = strchr(unc_name, '/')) ||
574				(share = strchr(unc_name,'\\'))) {
575				*share = 0;  /* temporarily terminate the string */
576				share += 1;
577				if(got_ip == 0) {
578					host_entry = gethostbyname(unc_name);
579				}
580				*(share - 1) = '/'; /* put the slash back */
581				if(got_ip) {
582					if(verboseflag)
583						printf("ip address specified explicitly\n");
584					return NULL;
585				}
586				if(host_entry == NULL) {
587					printf("mount error: could not find target server. TCP name %s not found ", unc_name);
588					printf(" rc = %d\n",rc);
589					return NULL;
590				} else {
591					/* BB should we pass an alternate version of the share name as Unicode */
592					/* BB what about ipv6? BB */
593					/* BB add retries with alternate servers in list */
594
595					memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
596
597					ipaddress_string = inet_ntoa(server_ipaddr);
598					if(ipaddress_string == NULL) {
599						printf("mount error: could not get valid ip address for target server\n");
600						return NULL;
601					}
602					return ipaddress_string;
603				}
604			} else {
605				/* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
606				printf("Mounting the DFS root for a particular server not implemented yet\n");
607				return NULL;
608			}
609		}
610	}
611}
612
613static struct option longopts[] = {
614	{ "all", 0, NULL, 'a' },
615	{ "help",0, NULL, 'h' },
616	{ "move",0, NULL, 'm' },
617	{ "bind",0, NULL, 'b' },
618	{ "read-only", 0, NULL, 'r' },
619	{ "ro", 0, NULL, 'r' },
620	{ "verbose", 0, NULL, 'v' },
621	{ "version", 0, NULL, 'V' },
622	{ "read-write", 0, NULL, 'w' },
623	{ "rw", 0, NULL, 'w' },
624	{ "options", 1, NULL, 'o' },
625	{ "type", 1, NULL, 't' },
626	{ "rsize",1, NULL, 'R' },
627	{ "wsize",1, NULL, 'W' },
628	{ "uid", 1, NULL, '1'},
629	{ "gid", 1, NULL, '2'},
630	{ "user",1,NULL,'u'},
631	{ "username",1,NULL,'u'},
632	{ "dom",1,NULL,'d'},
633	{ "domain",1,NULL,'d'},
634	{ "password",1,NULL,'p'},
635	{ "pass",1,NULL,'p'},
636	{ "credentials",1,NULL,'c'},
637	{ "port",1,NULL,'P'},
638	/* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
639	{ NULL, 0, NULL, 0 }
640};
641
642int main(int argc, char ** argv)
643{
644	int c;
645	int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
646	char * orgoptions = NULL;
647	char * share_name = NULL;
648	char * domain_name = NULL;
649	char * ipaddr = NULL;
650	char * uuid = NULL;
651	char * mountpoint;
652	char * options;
653	char * resolved_path;
654	char * temp;
655	int rc;
656	int rsize = 0;
657	int wsize = 0;
658	int nomtab = 0;
659	int uid = 0;
660	int gid = 0;
661	int optlen = 0;
662	int orgoptlen = 0;
663	int retry = 0; /* set when we have to retry mount with uppercase */
664	struct stat statbuf;
665	struct utsname sysinfo;
666	struct mntent mountent;
667	FILE * pmntfile;
668
669	/* setlocale(LC_ALL, "");
670	bindtextdomain(PACKAGE, LOCALEDIR);
671	textdomain(PACKAGE); */
672
673	if(argc && argv) {
674		thisprogram = argv[0];
675	}
676	if(thisprogram == NULL)
677		thisprogram = "mount.cifs";
678
679	uname(&sysinfo);
680	/* BB add workstation name and domain and pass down */
681
682/* #ifdef _GNU_SOURCE
683	printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
684#endif */
685
686	share_name = argv[1];
687	mountpoint = argv[2];
688
689	/* add sharename in opts string as unc= parm */
690
691	while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
692			 longopts, NULL)) != -1) {
693		switch (c) {
694/* No code to do the following  options yet */
695/*	case 'l':
696		list_with_volumelabel = 1;
697		break;
698	case 'L':
699		volumelabel = optarg;
700		break; */
701/*	case 'a':
702		++mount_all;
703		break; */
704
705		case '?':
706		case 'h':	 /* help */
707			mount_cifs_usage ();
708			exit(1);
709		case 'n':
710		    ++nomtab;
711		    break;
712		case 'b':
713			flags |= MS_BIND;
714			break;
715		case 'm':
716			flags |= MS_MOVE;
717			break;
718		case 'o':
719			orgoptions = strdup(optarg);
720		    break;
721		case 'r':  /* mount readonly */
722			flags |= MS_RDONLY;
723			break;
724		case 'U':
725			uuid = optarg;
726			break;
727		case 'v':
728			++verboseflag;
729			break;
730		case 'V':
731			printf ("mount.cifs version: %s.%s%s\n",
732			MOUNT_CIFS_VERSION_MAJOR,
733			MOUNT_CIFS_VERSION_MINOR,
734			MOUNT_CIFS_VENDOR_SUFFIX);
735			if(mountpassword) {
736				memset(mountpassword,0,64);
737			}
738			exit (0);
739		case 'w':
740			flags &= ~MS_RDONLY;
741			break;
742		case 'R':
743			rsize = atoi(optarg) ;
744			break;
745		case 'W':
746			wsize = atoi(optarg);
747			break;
748		case '1':
749			uid = atoi(optarg);
750			break;
751		case '2':
752			gid = atoi(optarg);
753			break;
754		case 'u':
755			got_user = 1;
756			user_name = optarg;
757			break;
758		case 'd':
759			domain_name = optarg;
760			break;
761		case 'p':
762			if(mountpassword == NULL)
763				mountpassword = calloc(65,1);
764			if(mountpassword) {
765				got_password = 1;
766				strncpy(mountpassword,optarg,64);
767			}
768			break;
769		case 'S':
770			get_password_from_file(0 /* stdin */,NULL);
771			break;
772		case 't':
773			break;
774		default:
775			printf("unknown mount option %c\n",c);
776			mount_cifs_usage();
777			exit(1);
778		}
779	}
780
781	if(argc < 3)
782		mount_cifs_usage();
783
784	if (getenv("PASSWD")) {
785		if(mountpassword == NULL)
786			mountpassword = calloc(65,1);
787		if(mountpassword) {
788			strncpy(mountpassword,getenv("PASSWD"),64);
789			got_password = 1;
790		}
791	} else if (getenv("PASSWD_FD")) {
792		get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
793	} else if (getenv("PASSWD_FILE")) {
794		get_password_from_file(0, getenv("PASSWD_FILE"));
795	}
796
797        if (orgoptions && parse_options(orgoptions, &flags))
798                return -1;
799
800	ipaddr = parse_server(&share_name);
801	if((ipaddr == NULL) && (got_ip == 0)) {
802		printf("No ip address specified and hostname not found\n");
803		return -1;
804	}
805
806
807	/* BB save off path and pop after mount returns? */
808	resolved_path = malloc(PATH_MAX+1);
809	if(resolved_path) {
810		/* Note that if we can not canonicalize the name, we get
811		another chance to see if it is valid when we chdir to it */
812		if (realpath(mountpoint, resolved_path)) {
813			mountpoint = resolved_path;
814		}
815	}
816	if(chdir(mountpoint)) {
817		printf("mount error: can not change directory into mount target %s\n",mountpoint);
818		return -1;
819	}
820
821	if(stat (".", &statbuf)) {
822		printf("mount error: mount point %s does not exist\n",mountpoint);
823		return -1;
824	}
825
826	if (S_ISDIR(statbuf.st_mode) == 0) {
827		printf("mount error: mount point %s is not a directory\n",mountpoint);
828		return -1;
829	}
830
831	if((getuid() != 0) && (geteuid() == 0)) {
832		if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
833#ifndef CIFS_ALLOW_USR_SUID
834			/* Do not allow user mounts to control suid flag
835			for mount unless explicitly built that way */
836			flags |= MS_NOSUID | MS_NODEV;
837#endif
838		} else {
839			printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
840			return -1;
841		}
842	}
843
844	if(got_user == 0)
845		user_name = getusername();
846
847	if(got_password == 0) {
848		mountpassword = getpass("Password: "); /* BB obsolete */
849		got_password = 1;
850	}
851	/* FIXME launch daemon (handles dfs name resolution and credential change)
852	   remember to clear parms and overwrite password field before launching */
853mount_retry:
854	if(orgoptions) {
855		optlen = strlen(orgoptions);
856		orgoptlen = optlen;
857	} else
858		optlen = 0;
859	if(share_name)
860		optlen += strlen(share_name) + 4;
861	if(user_name)
862		optlen += strlen(user_name) + 6;
863	if(ipaddr)
864		optlen += strlen(ipaddr) + 4;
865	if(mountpassword)
866		optlen += strlen(mountpassword) + 6;
867	options = malloc(optlen + 10);
868
869	if(options == NULL) {
870		printf("Could not allocate memory for mount options\n");
871		return -1;
872	}
873
874
875	options[0] = 0;
876	strncat(options,"unc=",4);
877	strcat(options,share_name);
878	/* scan backwards and reverse direction of slash */
879	temp = strrchr(options, '/');
880	if(temp > options + 6)
881		*temp = '\\';
882	if(ipaddr) {
883		strncat(options,",ip=",4);
884		strcat(options,ipaddr);
885	}
886	if(user_name) {
887		strncat(options,",user=",6);
888		strcat(options,user_name);
889	}
890	if(mountpassword) {
891		strncat(options,",pass=",6);
892		strcat(options,mountpassword);
893	}
894	strncat(options,",ver=",5);
895	strcat(options,MOUNT_CIFS_VERSION_MAJOR);
896
897	if(orgoptions) {
898		strcat(options,",");
899		strcat(options,orgoptions);
900	}
901	if(verboseflag)
902		printf("\nmount.cifs kernel mount options %s \n",options);
903	if(mount(share_name, mountpoint, "cifs", flags, options)) {
904	/* remember to kill daemon on error */
905		char * tmp;
906
907		switch (errno) {
908		case 0:
909			printf("mount failed but no error number set\n");
910			break;
911		case ENODEV:
912			printf("mount error: cifs filesystem not supported by the system\n");
913			break;
914		case ENXIO:
915			if(retry == 0) {
916				retry = 1;
917				tmp = share_name;
918				while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
919					*tmp = toupper((unsigned char)*tmp);
920		        		tmp++;
921				}
922				if(!*tmp) {
923					printf("retrying with upper case share name\n");
924					goto mount_retry;
925				}
926			}
927		default:
928
929			printf("mount error %d = %s\n",errno,strerror(errno));
930		}
931		printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
932		if(mountpassword) {
933			memset(mountpassword,0,64);
934		}
935		return -1;
936	} else {
937		pmntfile = setmntent(MOUNTED, "a+");
938		if(pmntfile) {
939			mountent.mnt_fsname = share_name;
940			mountent.mnt_dir = mountpoint;
941			mountent.mnt_type = "cifs";
942			mountent.mnt_opts = malloc(220);
943			if(mountent.mnt_opts) {
944				char * mount_user = getusername();
945				memset(mountent.mnt_opts,0,200);
946				if(flags & MS_RDONLY)
947					strcat(mountent.mnt_opts,"ro");
948				else
949					strcat(mountent.mnt_opts,"rw");
950				if(flags & MS_MANDLOCK)
951					strcat(mountent.mnt_opts,",mand");
952				else
953					strcat(mountent.mnt_opts,",nomand");
954				if(flags & MS_NOEXEC)
955					strcat(mountent.mnt_opts,",noexec");
956				if(flags & MS_NOSUID)
957					strcat(mountent.mnt_opts,",nosuid");
958				if(flags & MS_NODEV)
959					strcat(mountent.mnt_opts,",nodev");
960				if(flags & MS_SYNCHRONOUS)
961					strcat(mountent.mnt_opts,",synch");
962				if(mount_user) {
963					if(getuid() != 0) {
964						strcat(mountent.mnt_opts,",user=");
965						strcat(mountent.mnt_opts,mount_user);
966					}
967					free(mount_user);
968				}
969			}
970			mountent.mnt_freq = 0;
971			mountent.mnt_passno = 0;
972			rc = addmntent(pmntfile,&mountent);
973			endmntent(pmntfile);
974			if(mountent.mnt_opts)
975				free(mountent.mnt_opts);
976		} else {
977		    printf("could not update mount table\n");
978		}
979	}
980	if(mountpassword) {
981		memset(mountpassword,0,64);
982		free(mountpassword);
983	}
984
985	if(options) {
986		memset(options,0,optlen);
987		free(options);
988	}
989
990	if(orgoptions) {
991		memset(orgoptions,0,orgoptlen);
992		free(orgoptions);
993	}
994	if(resolved_path) {
995		free(resolved_path);
996	}
997
998	if(free_share_name) {
999		free(share_name);
1000		}
1001	return 0;
1002}
1003
1004