source: branches/samba-3.0/source/client/mount.cifs.c@ 293

Last change on this file since 293 was 165, checked in by Paul Smedley, 16 years ago

Add 'missing' 3.0.34 diffs

File size: 38.0 KB
Line 
1/*
2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2008 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 <grp.h>
28#include <ctype.h>
29#include <sys/types.h>
30#include <sys/mount.h>
31#include <sys/stat.h>
32#include <sys/utsname.h>
33#include <sys/socket.h>
34#include <arpa/inet.h>
35#include <getopt.h>
36#include <errno.h>
37#include <netdb.h>
38#include <string.h>
39#include <mntent.h>
40#include <fcntl.h>
41#include <limits.h>
42#include "mount.h"
43
44#define MOUNT_CIFS_VERSION_MAJOR "1"
45#define MOUNT_CIFS_VERSION_MINOR "12"
46
47#ifndef MOUNT_CIFS_VENDOR_SUFFIX
48 #ifdef _SAMBA_BUILD_
49 #include "include/version.h"
50 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
51 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
52 #else
53 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
54 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
55 #else
56 #define MOUNT_CIFS_VENDOR_SUFFIX ""
57 #endif /* _SAMBA_BUILD_ */
58#endif /* MOUNT_CIFS_VENDOR_SUFFIX */
59
60#ifndef MS_MOVE
61#define MS_MOVE 8192
62#endif
63
64#ifndef MS_BIND
65#define MS_BIND 4096
66#endif
67
68#define MAX_UNC_LEN 1024
69
70#define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
71
72const char *thisprogram;
73int verboseflag = 0;
74static int got_password = 0;
75static int got_user = 0;
76static int got_domain = 0;
77static int got_ip = 0;
78static int got_unc = 0;
79static int got_uid = 0;
80static int got_gid = 0;
81static char * user_name = NULL;
82static char * mountpassword = NULL;
83char * domain_name = NULL;
84char * prefixpath = NULL;
85
86/* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
87 * don't link to libreplace so need them here. */
88
89/* like strncpy but does not 0 fill the buffer and always null
90 * terminates. bufsize is the size of the destination buffer */
91static size_t strlcpy(char *d, const char *s, size_t bufsize)
92{
93 size_t len = strlen(s);
94 size_t ret = len;
95 if (bufsize <= 0) return 0;
96 if (len >= bufsize) len = bufsize-1;
97 memcpy(d, s, len);
98 d[len] = 0;
99 return ret;
100}
101
102/* like strncat but does not 0 fill the buffer and always null
103 * terminates. bufsize is the length of the buffer, which should
104 * be one more than the maximum resulting string length */
105static size_t strlcat(char *d, const char *s, size_t bufsize)
106{
107 size_t len1 = strlen(d);
108 size_t len2 = strlen(s);
109 size_t ret = len1 + len2;
110
111 if (len1+len2 >= bufsize) {
112 if (bufsize < (len1+1)) {
113 return ret;
114 }
115 len2 = bufsize - (len1+1);
116 }
117 if (len2 > 0) {
118 memcpy(d+len1, s, len2);
119 d[len1+len2] = 0;
120 }
121 return ret;
122}
123
124/* BB finish BB
125
126 cifs_umount
127 open nofollow - avoid symlink exposure?
128 get owner of dir see if matches self or if root
129 call system(umount argv) etc.
130
131BB end finish BB */
132
133static char * check_for_domain(char **);
134
135
136static void mount_cifs_usage(void)
137{
138 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
139 printf("\nMount the remote target, specified as a UNC name,");
140 printf(" to a local directory.\n\nOptions:\n");
141 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
142 printf("\nLess commonly used options:");
143 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
144 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
145 printf("\n\tdirectio,mapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
146 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
147 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
148 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
149 printf("\n\nRarely used options:");
150 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
151 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
152 printf("\n\tnointr,ignorecase,noposixpaths,noacl");
153 printf("\n\nOptions are described in more detail in the manual page");
154 printf("\n\tman 8 mount.cifs\n");
155 printf("\nTo display the version number of the mount helper:");
156 printf("\n\t%s -V\n",thisprogram);
157
158 if(mountpassword) {
159 memset(mountpassword,0,64);
160 free(mountpassword);
161 mountpassword = NULL;
162 }
163 exit(EX_USAGE);
164}
165
166/* caller frees username if necessary */
167static char * getusername(void) {
168 char *username = NULL;
169 struct passwd *password = getpwuid(getuid());
170
171 if (password) {
172 username = password->pw_name;
173 }
174 return username;
175}
176
177static char * parse_cifs_url(char * unc_name)
178{
179 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
180 return NULL;
181}
182
183static int open_cred_file(char * file_name)
184{
185 char * line_buf;
186 char * temp_val;
187 FILE * fs;
188 int i, length;
189 fs = fopen(file_name,"r");
190 if(fs == NULL)
191 return errno;
192 line_buf = (char *)malloc(4096);
193 if(line_buf == NULL) {
194 fclose(fs);
195 return -ENOMEM;
196 }
197
198 while(fgets(line_buf,4096,fs)) {
199 /* parse line from credential file */
200
201 /* eat leading white space */
202 for(i=0;i<4086;i++) {
203 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
204 break;
205 /* if whitespace - skip past it */
206 }
207 if (strncasecmp("username",line_buf+i,8) == 0) {
208 temp_val = strchr(line_buf + i,'=');
209 if(temp_val) {
210 /* go past equals sign */
211 temp_val++;
212 for(length = 0;length<4087;length++) {
213 if ((temp_val[length] == '\n')
214 || (temp_val[length] == '\0')) {
215 break;
216 }
217 }
218 if(length > 4086) {
219 printf("mount.cifs failed due to malformed username in credentials file");
220 memset(line_buf,0,4096);
221 if(mountpassword) {
222 memset(mountpassword,0,64);
223 }
224 exit(EX_USAGE);
225 } else {
226 got_user = 1;
227 user_name = (char *)calloc(1 + length,1);
228 /* BB adding free of user_name string before exit,
229 not really necessary but would be cleaner */
230 strncpy(user_name,temp_val, length);
231 }
232 }
233 } else if (strncasecmp("password",line_buf+i,8) == 0) {
234 temp_val = strchr(line_buf+i,'=');
235 if(temp_val) {
236 /* go past equals sign */
237 temp_val++;
238 for(length = 0;length<65;length++) {
239 if ((temp_val[length] == '\n')
240 || (temp_val[length] == '\0')) {
241 break;
242 }
243 }
244 if(length > 64) {
245 printf("mount.cifs failed: password in credentials file too long\n");
246 memset(line_buf,0, 4096);
247 if(mountpassword) {
248 memset(mountpassword,0,64);
249 }
250 exit(EX_USAGE);
251 } else {
252 if(mountpassword == NULL) {
253 mountpassword = (char *)calloc(65,1);
254 } else
255 memset(mountpassword,0,64);
256 if(mountpassword) {
257 strncpy(mountpassword,temp_val,length);
258 got_password = 1;
259 }
260 }
261 }
262 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
263 temp_val = strchr(line_buf+i,'=');
264 if(temp_val) {
265 /* go past equals sign */
266 temp_val++;
267 if(verboseflag)
268 printf("\nDomain %s\n",temp_val);
269 for(length = 0;length<65;length++) {
270 if ((temp_val[length] == '\n')
271 || (temp_val[length] == '\0')) {
272 break;
273 }
274 }
275 if(length > 64) {
276 printf("mount.cifs failed: domain in credentials file too long\n");
277 if(mountpassword) {
278 memset(mountpassword,0,64);
279 }
280 exit(EX_USAGE);
281 } else {
282 if(domain_name == NULL) {
283 domain_name = (char *)calloc(65,1);
284 } else
285 memset(domain_name,0,64);
286 if(domain_name) {
287 strncpy(domain_name,temp_val,length);
288 got_domain = 1;
289 }
290 }
291 }
292 }
293
294 }
295 fclose(fs);
296 if(line_buf) {
297 memset(line_buf,0,4096);
298 free(line_buf);
299 line_buf = NULL;
300 }
301 return 0;
302}
303
304static int get_password_from_file(int file_descript, char * filename)
305{
306 int rc = 0;
307 int i;
308 char c;
309
310 if(mountpassword == NULL)
311 mountpassword = (char *)calloc(65,1);
312 else
313 memset(mountpassword, 0, 64);
314
315 if (mountpassword == NULL) {
316 printf("malloc failed\n");
317 exit(EX_SYSERR);
318 }
319
320 if(filename != NULL) {
321 file_descript = open(filename, O_RDONLY);
322 if(file_descript < 0) {
323 printf("mount.cifs failed. %s attempting to open password file %s\n",
324 strerror(errno),filename);
325 exit(EX_SYSERR);
326 }
327 }
328 /* else file already open and fd provided */
329
330 for(i=0;i<64;i++) {
331 rc = read(file_descript,&c,1);
332 if(rc < 0) {
333 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
334 memset(mountpassword,0,64);
335 if(filename != NULL)
336 close(file_descript);
337 exit(EX_SYSERR);
338 } else if(rc == 0) {
339 if(mountpassword[0] == 0) {
340 if(verboseflag)
341 printf("\nWarning: null password used since cifs password file empty");
342 }
343 break;
344 } else /* read valid character */ {
345 if((c == 0) || (c == '\n')) {
346 break;
347 } else
348 mountpassword[i] = c;
349 }
350 }
351 if((i == 64) && (verboseflag)) {
352 printf("\nWarning: password longer than 64 characters specified in cifs password file");
353 }
354 got_password = 1;
355 if(filename != NULL) {
356 close(file_descript);
357 }
358
359 return rc;
360}
361
362static int parse_options(char ** optionsp, int * filesys_flags)
363{
364 const char * data;
365 char * percent_char = NULL;
366 char * value = NULL;
367 char * next_keyword = NULL;
368 char * out = NULL;
369 int out_len = 0;
370 int word_len;
371 int rc = 0;
372 char user[32];
373 char group[32];
374
375 if (!optionsp || !*optionsp)
376 return 1;
377 data = *optionsp;
378
379 if(verboseflag)
380 printf("parsing options: %s\n", data);
381
382 /* BB fixme check for separator override BB */
383
384 if (getuid()) {
385 got_uid = 1;
386 snprintf(user,sizeof(user),"%u",getuid());
387 got_gid = 1;
388 snprintf(group,sizeof(group),"%u",getgid());
389 }
390
391/* while ((data = strsep(&options, ",")) != NULL) { */
392 while(data != NULL) {
393 /* check if ends with trailing comma */
394 if(*data == 0)
395 break;
396
397 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
398 /* data = next keyword */
399 /* value = next value ie stuff after equal sign */
400
401 next_keyword = strchr(data,','); /* BB handle sep= */
402
403 /* temporarily null terminate end of keyword=value pair */
404 if(next_keyword)
405 *next_keyword++ = 0;
406
407 /* temporarily null terminate keyword to make keyword and value distinct */
408 if ((value = strchr(data, '=')) != NULL) {
409 *value = '\0';
410 value++;
411 }
412
413 if (strncmp(data, "users",5) == 0) {
414 if(!value || !*value) {
415 goto nocopy;
416 }
417 } else if (strncmp(data, "user_xattr",10) == 0) {
418 /* do nothing - need to skip so not parsed as user name */
419 } else if (strncmp(data, "user", 4) == 0) {
420
421 if (!value || !*value) {
422 if(data[4] == '\0') {
423 if(verboseflag)
424 printf("\nskipping empty user mount parameter\n");
425 /* remove the parm since it would otherwise be confusing
426 to the kernel code which would think it was a real username */
427 goto nocopy;
428 } else {
429 printf("username specified with no parameter\n");
430 return 1; /* needs_arg; */
431 }
432 } else {
433 if (strnlen(value, 260) < 260) {
434 got_user=1;
435 percent_char = strchr(value,'%');
436 if(percent_char) {
437 *percent_char = ',';
438 if(mountpassword == NULL)
439 mountpassword = (char *)calloc(65,1);
440 if(mountpassword) {
441 if(got_password)
442 printf("\nmount.cifs warning - password specified twice\n");
443 got_password = 1;
444 percent_char++;
445 strncpy(mountpassword, percent_char,64);
446 /* remove password from username */
447 while(*percent_char != 0) {
448 *percent_char = ',';
449 percent_char++;
450 }
451 }
452 }
453 /* this is only case in which the user
454 name buf is not malloc - so we have to
455 check for domain name embedded within
456 the user name here since the later
457 call to check_for_domain will not be
458 invoked */
459 domain_name = check_for_domain(&value);
460 } else {
461 printf("username too long\n");
462 return 1;
463 }
464 }
465 } else if (strncmp(data, "pass", 4) == 0) {
466 if (!value || !*value) {
467 if(got_password) {
468 printf("\npassword specified twice, ignoring second\n");
469 } else
470 got_password = 1;
471 } else if (strnlen(value, 17) < 17) {
472 if(got_password)
473 printf("\nmount.cifs warning - password specified twice\n");
474 got_password = 1;
475 } else {
476 printf("password too long\n");
477 return 1;
478 }
479 } else if (strncmp(data, "sec", 3) == 0) {
480 if (value) {
481 if (!strncmp(value, "none", 4) ||
482 !strncmp(value, "krb5", 4))
483 got_password = 1;
484 }
485 } else if (strncmp(data, "ip", 2) == 0) {
486 if (!value || !*value) {
487 printf("target ip address argument missing");
488 } else if (strnlen(value, 35) < 35) {
489 if(verboseflag)
490 printf("ip address %s override specified\n",value);
491 got_ip = 1;
492 } else {
493 printf("ip address too long\n");
494 return 1;
495 }
496 } else if ((strncmp(data, "unc", 3) == 0)
497 || (strncmp(data, "target", 6) == 0)
498 || (strncmp(data, "path", 4) == 0)) {
499 if (!value || !*value) {
500 printf("invalid path to network resource\n");
501 return 1; /* needs_arg; */
502 } else if(strnlen(value,5) < 5) {
503 printf("UNC name too short");
504 }
505
506 if (strnlen(value, 300) < 300) {
507 got_unc = 1;
508 if (strncmp(value, "//", 2) == 0) {
509 if(got_unc)
510 printf("unc name specified twice, ignoring second\n");
511 else
512 got_unc = 1;
513 } else if (strncmp(value, "\\\\", 2) != 0) {
514 printf("UNC Path does not begin with // or \\\\ \n");
515 return 1;
516 } else {
517 if(got_unc)
518 printf("unc name specified twice, ignoring second\n");
519 else
520 got_unc = 1;
521 }
522 } else {
523 printf("CIFS: UNC name too long\n");
524 return 1;
525 }
526 } else if ((strncmp(data, "domain", 3) == 0)
527 || (strncmp(data, "workgroup", 5) == 0)) {
528 if (!value || !*value) {
529 printf("CIFS: invalid domain name\n");
530 return 1; /* needs_arg; */
531 }
532 if (strnlen(value, 65) < 65) {
533 got_domain = 1;
534 } else {
535 printf("domain name too long\n");
536 return 1;
537 }
538 } else if (strncmp(data, "cred", 4) == 0) {
539 if (value && *value) {
540 rc = open_cred_file(value);
541 if(rc) {
542 printf("error %d opening credential file %s\n",rc, value);
543 return 1;
544 }
545 } else {
546 printf("invalid credential file name specified\n");
547 return 1;
548 }
549 } else if (strncmp(data, "uid", 3) == 0) {
550 if (value && *value) {
551 got_uid = 1;
552 if (!isdigit(*value)) {
553 struct passwd *pw;
554
555 if (!(pw = getpwnam(value))) {
556 printf("bad user name \"%s\"\n", value);
557 exit(EX_USAGE);
558 }
559 snprintf(user, sizeof(user), "%u", pw->pw_uid);
560 } else {
561 strlcpy(user,value,sizeof(user));
562 }
563 }
564 goto nocopy;
565 } else if (strncmp(data, "gid", 3) == 0) {
566 if (value && *value) {
567 got_gid = 1;
568 if (!isdigit(*value)) {
569 struct group *gr;
570
571 if (!(gr = getgrnam(value))) {
572 printf("bad group name \"%s\"\n", value);
573 exit(EX_USAGE);
574 }
575 snprintf(group, sizeof(group), "%u", gr->gr_gid);
576 } else {
577 strlcpy(group,value,sizeof(group));
578 }
579 }
580 goto nocopy;
581 /* fmask and dmask synonyms for people used to smbfs syntax */
582 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
583 if (!value || !*value) {
584 printf ("Option '%s' requires a numerical argument\n", data);
585 return 1;
586 }
587
588 if (value[0] != '0') {
589 printf ("WARNING: '%s' not expressed in octal.\n", data);
590 }
591
592 if (strcmp (data, "fmask") == 0) {
593 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
594 data = "file_mode"; /* BB fix this */
595 }
596 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
597 if (!value || !*value) {
598 printf ("Option '%s' requires a numerical argument\n", data);
599 return 1;
600 }
601
602 if (value[0] != '0') {
603 printf ("WARNING: '%s' not expressed in octal.\n", data);
604 }
605
606 if (strcmp (data, "dmask") == 0) {
607 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
608 data = "dir_mode";
609 }
610 /* the following eight mount options should be
611 stripped out from what is passed into the kernel
612 since these eight options are best passed as the
613 mount flags rather than redundantly to the kernel
614 and could generate spurious warnings depending on the
615 level of the corresponding cifs vfs kernel code */
616 } else if (strncmp(data, "nosuid", 6) == 0) {
617 *filesys_flags |= MS_NOSUID;
618 } else if (strncmp(data, "suid", 4) == 0) {
619 *filesys_flags &= ~MS_NOSUID;
620 } else if (strncmp(data, "nodev", 5) == 0) {
621 *filesys_flags |= MS_NODEV;
622 } else if ((strncmp(data, "nobrl", 5) == 0) ||
623 (strncmp(data, "nolock", 6) == 0)) {
624 *filesys_flags &= ~MS_MANDLOCK;
625 } else if (strncmp(data, "dev", 3) == 0) {
626 *filesys_flags &= ~MS_NODEV;
627 } else if (strncmp(data, "noexec", 6) == 0) {
628 *filesys_flags |= MS_NOEXEC;
629 } else if (strncmp(data, "exec", 4) == 0) {
630 *filesys_flags &= ~MS_NOEXEC;
631 } else if (strncmp(data, "guest", 5) == 0) {
632 got_password=1;
633 } else if (strncmp(data, "ro", 2) == 0) {
634 *filesys_flags |= MS_RDONLY;
635 } else if (strncmp(data, "rw", 2) == 0) {
636 *filesys_flags &= ~MS_RDONLY;
637 } else if (strncmp(data, "remount", 7) == 0) {
638 *filesys_flags |= MS_REMOUNT;
639 } /* else if (strnicmp(data, "port", 4) == 0) {
640 if (value && *value) {
641 vol->port =
642 simple_strtoul(value, &value, 0);
643 }
644 } else if (strnicmp(data, "rsize", 5) == 0) {
645 if (value && *value) {
646 vol->rsize =
647 simple_strtoul(value, &value, 0);
648 }
649 } else if (strnicmp(data, "wsize", 5) == 0) {
650 if (value && *value) {
651 vol->wsize =
652 simple_strtoul(value, &value, 0);
653 }
654 } else if (strnicmp(data, "version", 3) == 0) {
655 } else {
656 printf("CIFS: Unknown mount option %s\n",data);
657 } */ /* nothing to do on those four mount options above.
658 Just pass to kernel and ignore them here */
659
660 /* Copy (possibly modified) option to out */
661 word_len = strlen(data);
662 if (value)
663 word_len += 1 + strlen(value);
664
665 out = (char *)realloc(out, out_len + word_len + 2);
666 if (out == NULL) {
667 perror("malloc");
668 exit(EX_SYSERR);
669 }
670
671 if (out_len) {
672 strlcat(out, ",", out_len + word_len + 2);
673 out_len++;
674 }
675
676 if (value)
677 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
678 else
679 snprintf(out + out_len, word_len + 1, "%s", data);
680 out_len = strlen(out);
681
682nocopy:
683 data = next_keyword;
684 }
685
686 /* special-case the uid and gid */
687 if (got_uid) {
688 word_len = strlen(user);
689
690 out = (char *)realloc(out, out_len + word_len + 6);
691 if (out == NULL) {
692 perror("malloc");
693 exit(EX_SYSERR);
694 }
695
696 if (out_len) {
697 strlcat(out, ",", out_len + word_len + 6);
698 out_len++;
699 }
700 snprintf(out + out_len, word_len + 5, "uid=%s", user);
701 out_len = strlen(out);
702 }
703 if (got_gid) {
704 word_len = strlen(group);
705
706 out = (char *)realloc(out, out_len + 1 + word_len + 6);
707 if (out == NULL) {
708 perror("malloc");
709 exit(EX_SYSERR);
710 }
711
712 if (out_len) {
713 strlcat(out, ",", out_len + word_len + 6);
714 out_len++;
715 }
716 snprintf(out + out_len, word_len + 5, "gid=%s", group);
717 out_len = strlen(out);
718 }
719
720 free(*optionsp);
721 *optionsp = out;
722 return 0;
723}
724
725/* replace all (one or more) commas with double commas */
726static void check_for_comma(char ** ppasswrd)
727{
728 char *new_pass_buf;
729 char *pass;
730 int i,j;
731 int number_of_commas = 0;
732 int len;
733
734 if(ppasswrd == NULL)
735 return;
736 else
737 (pass = *ppasswrd);
738
739 len = strlen(pass);
740
741 for(i=0;i<len;i++) {
742 if(pass[i] == ',')
743 number_of_commas++;
744 }
745
746 if(number_of_commas == 0)
747 return;
748 if(number_of_commas > 64) {
749 /* would otherwise overflow the mount options buffer */
750 printf("\nInvalid password. Password contains too many commas.\n");
751 return;
752 }
753
754 new_pass_buf = (char *)malloc(len+number_of_commas+1);
755 if(new_pass_buf == NULL)
756 return;
757
758 for(i=0,j=0;i<len;i++,j++) {
759 new_pass_buf[j] = pass[i];
760 if(pass[i] == ',') {
761 j++;
762 new_pass_buf[j] = pass[i];
763 }
764 }
765 new_pass_buf[len+number_of_commas] = 0;
766
767 free(*ppasswrd);
768 *ppasswrd = new_pass_buf;
769
770 return;
771}
772
773/* Usernames can not have backslash in them and we use
774 [BB check if usernames can have forward slash in them BB]
775 backslash as domain\user separator character
776*/
777static char * check_for_domain(char **ppuser)
778{
779 char * original_string;
780 char * usernm;
781 char * domainnm;
782 int original_len;
783 int len;
784 int i;
785
786 if(ppuser == NULL)
787 return NULL;
788
789 original_string = *ppuser;
790
791 if (original_string == NULL)
792 return NULL;
793
794 original_len = strlen(original_string);
795
796 usernm = strchr(*ppuser,'/');
797 if (usernm == NULL) {
798 usernm = strchr(*ppuser,'\\');
799 if (usernm == NULL)
800 return NULL;
801 }
802
803 if(got_domain) {
804 printf("Domain name specified twice. Username probably malformed\n");
805 return NULL;
806 }
807
808 usernm[0] = 0;
809 domainnm = *ppuser;
810 if (domainnm[0] != 0) {
811 got_domain = 1;
812 } else {
813 printf("null domain\n");
814 }
815 len = strlen(domainnm);
816 /* reset domainm to new buffer, and copy
817 domain name into it */
818 domainnm = (char *)malloc(len+1);
819 if(domainnm == NULL)
820 return NULL;
821
822 strlcpy(domainnm,*ppuser,len+1);
823
824/* move_string(*ppuser, usernm+1) */
825 len = strlen(usernm+1);
826
827 if(len >= original_len) {
828 /* should not happen */
829 return domainnm;
830 }
831
832 for(i=0;i<original_len;i++) {
833 if(i<len)
834 original_string[i] = usernm[i+1];
835 else /* stuff with commas to remove last parm */
836 original_string[i] = ',';
837 }
838
839 /* BB add check for more than one slash?
840 strchr(*ppuser,'/');
841 strchr(*ppuser,'\\')
842 */
843
844 return domainnm;
845}
846
847/* replace all occurances of "from" in a string with "to" */
848static void replace_char(char *string, char from, char to, int maxlen)
849{
850 char *lastchar = string + maxlen;
851 while (string) {
852 string = strchr(string, from);
853 if (string) {
854 *string = to;
855 if (string >= lastchar)
856 return;
857 }
858 }
859}
860
861/* Note that caller frees the returned buffer if necessary */
862static char * parse_server(char ** punc_name)
863{
864 char * unc_name = *punc_name;
865 int length = strnlen(unc_name, MAX_UNC_LEN);
866 char * share;
867 char * ipaddress_string = NULL;
868 struct hostent * host_entry = NULL;
869 struct in_addr server_ipaddr;
870
871 if(length > (MAX_UNC_LEN - 1)) {
872 printf("mount error: UNC name too long");
873 return NULL;
874 }
875 if (strncasecmp("cifs://",unc_name,7) == 0)
876 return parse_cifs_url(unc_name+7);
877 if (strncasecmp("smb://",unc_name,6) == 0) {
878 return parse_cifs_url(unc_name+6);
879 }
880
881 if(length < 3) {
882 /* BB add code to find DFS root here */
883 printf("\nMounting the DFS root for domain not implemented yet\n");
884 return NULL;
885 } else {
886 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
887 /* check for nfs syntax ie server:share */
888 share = strchr(unc_name,':');
889 if(share) {
890 *punc_name = (char *)malloc(length+3);
891 if(*punc_name == NULL) {
892 /* put the original string back if
893 no memory left */
894 *punc_name = unc_name;
895 return NULL;
896 }
897 *share = '/';
898 strncpy((*punc_name)+2,unc_name,length);
899 free(unc_name);
900 unc_name = *punc_name;
901 unc_name[length+2] = 0;
902 goto continue_unc_parsing;
903 } else {
904 printf("mount error: improperly formatted UNC name.");
905 printf(" %s does not begin with \\\\ or //\n",unc_name);
906 return NULL;
907 }
908 } else {
909continue_unc_parsing:
910 unc_name[0] = '/';
911 unc_name[1] = '/';
912 unc_name += 2;
913
914 /* allow for either delimiter between host and sharename */
915 if ((share = strpbrk(unc_name, "/\\"))) {
916 *share = 0; /* temporarily terminate the string */
917 share += 1;
918 if(got_ip == 0) {
919 host_entry = gethostbyname(unc_name);
920 }
921 *(share - 1) = '/'; /* put delimiter back */
922
923 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
924 if ((prefixpath = strpbrk(share, "/\\"))) {
925 *prefixpath = 0; /* permanently terminate the string */
926 if (!strlen(++prefixpath))
927 prefixpath = NULL; /* this needs to be done explicitly */
928 }
929 if(got_ip) {
930 if(verboseflag)
931 printf("ip address specified explicitly\n");
932 return NULL;
933 }
934 if(host_entry == NULL) {
935 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
936 return NULL;
937 } else {
938 /* BB should we pass an alternate version of the share name as Unicode */
939 /* BB what about ipv6? BB */
940 /* BB add retries with alternate servers in list */
941
942 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
943
944 ipaddress_string = inet_ntoa(server_ipaddr);
945 if(ipaddress_string == NULL) {
946 printf("mount error: could not get valid ip address for target server\n");
947 return NULL;
948 }
949 return ipaddress_string;
950 }
951 } else {
952 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
953 printf("Mounting the DFS root for a particular server not implemented yet\n");
954 return NULL;
955 }
956 }
957 }
958}
959
960static struct option longopts[] = {
961 { "all", 0, NULL, 'a' },
962 { "help",0, NULL, 'h' },
963 { "move",0, NULL, 'm' },
964 { "bind",0, NULL, 'b' },
965 { "read-only", 0, NULL, 'r' },
966 { "ro", 0, NULL, 'r' },
967 { "verbose", 0, NULL, 'v' },
968 { "version", 0, NULL, 'V' },
969 { "read-write", 0, NULL, 'w' },
970 { "rw", 0, NULL, 'w' },
971 { "options", 1, NULL, 'o' },
972 { "type", 1, NULL, 't' },
973 { "rsize",1, NULL, 'R' },
974 { "wsize",1, NULL, 'W' },
975 { "uid", 1, NULL, '1'},
976 { "gid", 1, NULL, '2'},
977 { "user",1,NULL,'u'},
978 { "username",1,NULL,'u'},
979 { "dom",1,NULL,'d'},
980 { "domain",1,NULL,'d'},
981 { "password",1,NULL,'p'},
982 { "pass",1,NULL,'p'},
983 { "credentials",1,NULL,'c'},
984 { "port",1,NULL,'P'},
985 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
986 { NULL, 0, NULL, 0 }
987};
988
989/* convert a string to uppercase. return false if the string
990 * wasn't ASCII. Return success on a NULL ptr */
991static int
992uppercase_string(char *string)
993{
994 if (!string)
995 return 1;
996
997 while (*string) {
998 /* check for unicode */
999 if ((unsigned char) string[0] & 0x80)
1000 return 0;
1001 *string = toupper((unsigned char) *string);
1002 string++;
1003 }
1004
1005 return 1;
1006}
1007
1008int main(int argc, char ** argv)
1009{
1010 int c;
1011 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1012 char * orgoptions = NULL;
1013 char * share_name = NULL;
1014 char * ipaddr = NULL;
1015 char * uuid = NULL;
1016 char * mountpoint = NULL;
1017 char * options = NULL;
1018 char * resolved_path = NULL;
1019 char * temp;
1020 char * dev_name;
1021 int rc;
1022 int rsize = 0;
1023 int wsize = 0;
1024 int nomtab = 0;
1025 int uid = 0;
1026 int gid = 0;
1027 int optlen = 0;
1028 int orgoptlen = 0;
1029 size_t options_size = 0;
1030 int retry = 0; /* set when we have to retry mount with uppercase */
1031 struct stat statbuf;
1032 struct utsname sysinfo;
1033 struct mntent mountent;
1034 FILE * pmntfile;
1035
1036 /* setlocale(LC_ALL, "");
1037 bindtextdomain(PACKAGE, LOCALEDIR);
1038 textdomain(PACKAGE); */
1039
1040 if(argc && argv) {
1041 thisprogram = argv[0];
1042 } else {
1043 mount_cifs_usage();
1044 exit(EX_USAGE);
1045 }
1046
1047 if(thisprogram == NULL)
1048 thisprogram = "mount.cifs";
1049
1050 uname(&sysinfo);
1051 /* BB add workstation name and domain and pass down */
1052
1053/* #ifdef _GNU_SOURCE
1054 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1055#endif */
1056 if(argc > 2) {
1057 dev_name = argv[1];
1058 share_name = strndup(argv[1], MAX_UNC_LEN);
1059 if (share_name == NULL) {
1060 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1061 exit(EX_SYSERR);
1062 }
1063 mountpoint = argv[2];
1064 } else {
1065 mount_cifs_usage();
1066 exit(EX_USAGE);
1067 }
1068
1069 /* add sharename in opts string as unc= parm */
1070
1071 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1072 longopts, NULL)) != -1) {
1073 switch (c) {
1074/* No code to do the following options yet */
1075/* case 'l':
1076 list_with_volumelabel = 1;
1077 break;
1078 case 'L':
1079 volumelabel = optarg;
1080 break; */
1081/* case 'a':
1082 ++mount_all;
1083 break; */
1084
1085 case '?':
1086 case 'h': /* help */
1087 mount_cifs_usage ();
1088 exit(EX_USAGE);
1089 case 'n':
1090 ++nomtab;
1091 break;
1092 case 'b':
1093#ifdef MS_BIND
1094 flags |= MS_BIND;
1095#else
1096 fprintf(stderr,
1097 "option 'b' (MS_BIND) not supported\n");
1098#endif
1099 break;
1100 case 'm':
1101#ifdef MS_MOVE
1102 flags |= MS_MOVE;
1103#else
1104 fprintf(stderr,
1105 "option 'm' (MS_MOVE) not supported\n");
1106#endif
1107 break;
1108 case 'o':
1109 orgoptions = strdup(optarg);
1110 break;
1111 case 'r': /* mount readonly */
1112 flags |= MS_RDONLY;
1113 break;
1114 case 'U':
1115 uuid = optarg;
1116 break;
1117 case 'v':
1118 ++verboseflag;
1119 break;
1120 case 'V':
1121 printf ("mount.cifs version: %s.%s%s\n",
1122 MOUNT_CIFS_VERSION_MAJOR,
1123 MOUNT_CIFS_VERSION_MINOR,
1124 MOUNT_CIFS_VENDOR_SUFFIX);
1125 if(mountpassword) {
1126 memset(mountpassword,0,64);
1127 }
1128 exit (0);
1129 case 'w':
1130 flags &= ~MS_RDONLY;
1131 break;
1132 case 'R':
1133 rsize = atoi(optarg) ;
1134 break;
1135 case 'W':
1136 wsize = atoi(optarg);
1137 break;
1138 case '1':
1139 if (isdigit(*optarg)) {
1140 char *ep;
1141
1142 uid = strtoul(optarg, &ep, 10);
1143 if (*ep) {
1144 printf("bad uid value \"%s\"\n", optarg);
1145 exit(EX_USAGE);
1146 }
1147 } else {
1148 struct passwd *pw;
1149
1150 if (!(pw = getpwnam(optarg))) {
1151 printf("bad user name \"%s\"\n", optarg);
1152 exit(EX_USAGE);
1153 }
1154 uid = pw->pw_uid;
1155 endpwent();
1156 }
1157 break;
1158 case '2':
1159 if (isdigit(*optarg)) {
1160 char *ep;
1161
1162 gid = strtoul(optarg, &ep, 10);
1163 if (*ep) {
1164 printf("bad gid value \"%s\"\n", optarg);
1165 exit(EX_USAGE);
1166 }
1167 } else {
1168 struct group *gr;
1169
1170 if (!(gr = getgrnam(optarg))) {
1171 printf("bad user name \"%s\"\n", optarg);
1172 exit(EX_USAGE);
1173 }
1174 gid = gr->gr_gid;
1175 endpwent();
1176 }
1177 break;
1178 case 'u':
1179 got_user = 1;
1180 user_name = optarg;
1181 break;
1182 case 'd':
1183 domain_name = optarg; /* BB fix this - currently ignored */
1184 got_domain = 1;
1185 break;
1186 case 'p':
1187 if(mountpassword == NULL)
1188 mountpassword = (char *)calloc(65,1);
1189 if(mountpassword) {
1190 got_password = 1;
1191 strncpy(mountpassword,optarg,64);
1192 }
1193 break;
1194 case 'S':
1195 get_password_from_file(0 /* stdin */,NULL);
1196 break;
1197 case 't':
1198 break;
1199 default:
1200 printf("unknown mount option %c\n",c);
1201 mount_cifs_usage();
1202 exit(EX_USAGE);
1203 }
1204 }
1205
1206 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1207 mount_cifs_usage();
1208 exit(EX_USAGE);
1209 }
1210
1211 if (getenv("PASSWD")) {
1212 if(mountpassword == NULL)
1213 mountpassword = (char *)calloc(65,1);
1214 if(mountpassword) {
1215 strncpy(mountpassword,getenv("PASSWD"),64);
1216 got_password = 1;
1217 }
1218 } else if (getenv("PASSWD_FD")) {
1219 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1220 } else if (getenv("PASSWD_FILE")) {
1221 get_password_from_file(0, getenv("PASSWD_FILE"));
1222 }
1223
1224 if (orgoptions && parse_options(&orgoptions, &flags)) {
1225 rc = EX_USAGE;
1226 goto mount_exit;
1227 }
1228 ipaddr = parse_server(&share_name);
1229 if((ipaddr == NULL) && (got_ip == 0)) {
1230 printf("No ip address specified and hostname not found\n");
1231 rc = EX_USAGE;
1232 goto mount_exit;
1233 }
1234
1235 /* BB save off path and pop after mount returns? */
1236 resolved_path = (char *)malloc(PATH_MAX+1);
1237 if(resolved_path) {
1238 /* Note that if we can not canonicalize the name, we get
1239 another chance to see if it is valid when we chdir to it */
1240 if (realpath(mountpoint, resolved_path)) {
1241 mountpoint = resolved_path;
1242 }
1243 }
1244 if(chdir(mountpoint)) {
1245 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1246 rc = EX_USAGE;
1247 goto mount_exit;
1248 }
1249
1250 if(stat (".", &statbuf)) {
1251 printf("mount error: mount point %s does not exist\n",mountpoint);
1252 rc = EX_USAGE;
1253 goto mount_exit;
1254 }
1255
1256 if (S_ISDIR(statbuf.st_mode) == 0) {
1257 printf("mount error: mount point %s is not a directory\n",mountpoint);
1258 rc = EX_USAGE;
1259 goto mount_exit;
1260 }
1261
1262 if((getuid() != 0) && (geteuid() == 0)) {
1263 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1264#ifndef CIFS_ALLOW_USR_SUID
1265 /* Do not allow user mounts to control suid flag
1266 for mount unless explicitly built that way */
1267 flags |= MS_NOSUID | MS_NODEV;
1268#endif
1269 } else {
1270 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1271 exit(EX_USAGE);
1272 }
1273 }
1274
1275 if(got_user == 0) {
1276 user_name = getusername();
1277 got_user = 1;
1278 }
1279
1280 if(got_password == 0) {
1281 char *tmp_pass;
1282 tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1283 no good replacement yet */
1284 mountpassword = (char *)calloc(65,1);
1285 if (!tmp_pass || !mountpassword) {
1286 printf("Password not entered, exiting\n");
1287 exit(EX_USAGE);
1288 }
1289 strncpy(mountpassword, tmp_pass, 64);
1290
1291 got_password = 1;
1292 }
1293 /* FIXME launch daemon (handles dfs name resolution and credential change)
1294 remember to clear parms and overwrite password field before launching */
1295mount_retry:
1296 if(orgoptions) {
1297 optlen = strlen(orgoptions);
1298 orgoptlen = optlen;
1299 } else
1300 optlen = 0;
1301 if(share_name)
1302 optlen += strlen(share_name) + 4;
1303 else {
1304 printf("No server share name specified\n");
1305 printf("\nMounting the DFS root for server not implemented yet\n");
1306 exit(EX_USAGE);
1307 }
1308 if(user_name)
1309 optlen += strlen(user_name) + 6;
1310 if(ipaddr)
1311 optlen += strlen(ipaddr) + 4;
1312 if(mountpassword)
1313 optlen += strlen(mountpassword) + 6;
1314 if(options) {
1315 free(options);
1316 options = NULL;
1317 }
1318 options_size = optlen + 10 + 64;
1319 options = (char *)malloc(options_size /* space for commas in password */ + 8 /* space for domain= , domain name itself was counted as part of the length username string above */);
1320
1321 if(options == NULL) {
1322 printf("Could not allocate memory for mount options\n");
1323 exit(EX_SYSERR);
1324 }
1325
1326 options[0] = 0;
1327 strlcpy(options,"unc=",options_size);
1328 strlcat(options,share_name,options_size);
1329 /* scan backwards and reverse direction of slash */
1330 temp = strrchr(options, '/');
1331 if(temp > options + 6)
1332 *temp = '\\';
1333 if(ipaddr) {
1334 strlcat(options,",ip=",options_size);
1335 strlcat(options,ipaddr,options_size);
1336 }
1337
1338 if(user_name) {
1339 /* check for syntax like user=domain\user */
1340 if(got_domain == 0)
1341 domain_name = check_for_domain(&user_name);
1342 strlcat(options,",user=",options_size);
1343 strlcat(options,user_name,options_size);
1344 }
1345 if(retry == 0) {
1346 if(domain_name) {
1347 /* extra length accounted for in option string above */
1348 strlcat(options,",domain=",options_size);
1349 strlcat(options,domain_name,options_size);
1350 }
1351 }
1352 if(mountpassword) {
1353 /* Commas have to be doubled, or else they will
1354 look like the parameter separator */
1355/* if(sep is not set)*/
1356 if(retry == 0)
1357 check_for_comma(&mountpassword);
1358 strlcat(options,",pass=",options_size);
1359 strlcat(options,mountpassword,options_size);
1360 }
1361
1362 strlcat(options,",ver=",options_size);
1363 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1364
1365 if(orgoptions) {
1366 strlcat(options,",",options_size);
1367 strlcat(options,orgoptions,options_size);
1368 }
1369 if(prefixpath) {
1370 strlcat(options,",prefixpath=",options_size);
1371 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1372 }
1373 if(verboseflag)
1374 printf("\nmount.cifs kernel mount options %s \n",options);
1375
1376 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1377 replace_char(dev_name, '\\', '/', strlen(share_name));
1378
1379 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1380 /* remember to kill daemon on error */
1381 switch (errno) {
1382 case 0:
1383 printf("mount failed but no error number set\n");
1384 break;
1385 case ENODEV:
1386 printf("mount error: cifs filesystem not supported by the system\n");
1387 break;
1388 case ENXIO:
1389 if(retry == 0) {
1390 retry = 1;
1391 if (uppercase_string(dev_name) &&
1392 uppercase_string(share_name) &&
1393 uppercase_string(prefixpath)) {
1394 printf("retrying with upper case share name\n");
1395 goto mount_retry;
1396 }
1397 }
1398 default:
1399 printf("mount error %d = %s\n",errno,strerror(errno));
1400 }
1401 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1402 rc = EX_FAIL;
1403 } else {
1404 atexit(unlock_mtab);
1405 rc = lock_mtab();
1406 if (rc) {
1407 printf("cannot lock mtab");
1408 goto mount_exit;
1409 }
1410 pmntfile = setmntent(MOUNTED, "a+");
1411 if (!pmntfile) {
1412 printf("could not update mount table\n");
1413 unlock_mtab();
1414 rc = EX_FILEIO;
1415 goto mount_exit;
1416 }
1417 mountent.mnt_fsname = dev_name;
1418 mountent.mnt_dir = mountpoint;
1419 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1420 mountent.mnt_opts = (char *)malloc(220);
1421 if(mountent.mnt_opts) {
1422 char * mount_user = getusername();
1423 memset(mountent.mnt_opts,0,200);
1424 if(flags & MS_RDONLY)
1425 strlcat(mountent.mnt_opts,"ro",220);
1426 else
1427 strlcat(mountent.mnt_opts,"rw",220);
1428 if(flags & MS_MANDLOCK)
1429 strlcat(mountent.mnt_opts,",mand",220);
1430 if(flags & MS_NOEXEC)
1431 strlcat(mountent.mnt_opts,",noexec",220);
1432 if(flags & MS_NOSUID)
1433 strlcat(mountent.mnt_opts,",nosuid",220);
1434 if(flags & MS_NODEV)
1435 strlcat(mountent.mnt_opts,",nodev",220);
1436 if(flags & MS_SYNCHRONOUS)
1437 strlcat(mountent.mnt_opts,",sync",220);
1438 if(mount_user) {
1439 if(getuid() != 0) {
1440 strlcat(mountent.mnt_opts,
1441 ",user=", 220);
1442 strlcat(mountent.mnt_opts,
1443 mount_user, 220);
1444 }
1445 }
1446 }
1447 mountent.mnt_freq = 0;
1448 mountent.mnt_passno = 0;
1449 rc = addmntent(pmntfile,&mountent);
1450 endmntent(pmntfile);
1451 unlock_mtab();
1452 if(mountent.mnt_opts) {
1453 free(mountent.mnt_opts);
1454 mountent.mnt_opts = NULL;
1455 }
1456 if (rc)
1457 rc = EX_FILEIO;
1458 }
1459mount_exit:
1460 if(mountpassword) {
1461 int len = strlen(mountpassword);
1462 memset(mountpassword,0,len);
1463 free(mountpassword);
1464 mountpassword = NULL;
1465 }
1466
1467 if(options) {
1468 memset(options,0,optlen);
1469 free(options);
1470 options = NULL;
1471 }
1472
1473 if(orgoptions) {
1474 memset(orgoptions,0,orgoptlen);
1475 free(orgoptions);
1476 orgoptions = NULL;
1477 }
1478 if(resolved_path) {
1479 free(resolved_path);
1480 resolved_path = NULL;
1481 }
1482
1483 free(share_name);
1484 exit(rc);
1485}
Note: See TracBrowser for help on using the repository browser.