s3: Fix double free in net usershare.
[kai/samba.git] / source3 / utils / net_usershare.c
1 /*
2    Samba Unix/Linux SMB client library
3    Distributed SMB/CIFS Server Management Utility
4
5    Copyright (C) Jeremy Allison (jra@samba.org) 2005
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "utils/net.h"
23
24 struct {
25         const char *us_errstr;
26         enum usershare_err us_err;
27 } us_errs [] = {
28         {"",USERSHARE_OK},
29         {"Malformed usershare file", USERSHARE_MALFORMED_FILE},
30         {"Bad version number", USERSHARE_BAD_VERSION},
31         {"Malformed path entry", USERSHARE_MALFORMED_PATH},
32         {"Malformed comment entryfile", USERSHARE_MALFORMED_COMMENT_DEF},
33         {"Malformed acl definition", USERSHARE_MALFORMED_ACL_DEF},
34         {"Acl parse error", USERSHARE_ACL_ERR},
35         {"Path not absolute", USERSHARE_PATH_NOT_ABSOLUTE},
36         {"Path is denied", USERSHARE_PATH_IS_DENIED},
37         {"Path not allowed", USERSHARE_PATH_NOT_ALLOWED},
38         {"Path is not a directory", USERSHARE_PATH_NOT_DIRECTORY},
39         {"System error", USERSHARE_POSIX_ERR},
40         {NULL,(enum usershare_err)-1}
41 };
42
43 static const char *get_us_error_code(enum usershare_err us_err)
44 {
45         char *result;
46         int idx = 0;
47
48         while (us_errs[idx].us_errstr != NULL) {
49                 if (us_errs[idx].us_err == us_err) {
50                         return us_errs[idx].us_errstr;
51                 }
52                 idx++;
53         }
54
55         result = talloc_asprintf(talloc_tos(), "Usershare error code (0x%x)",
56                                  (unsigned int)us_err);
57         SMB_ASSERT(result != NULL);
58         return result;
59 }
60
61 /* The help subsystem for the USERSHARE subcommand */
62
63 static int net_usershare_add_usage(struct net_context *c, int argc, const char **argv)
64 {
65         char chr = *lp_winbind_separator();
66         d_printf(
67                 "net usershare add [-l|--long] <sharename> <path> [<comment>] [<acl>] [<guest_ok=[y|n]>]\n"
68                 "\tAdds the specified share name for this user.\n"
69                 "\t<sharename> is the new share name.\n"
70                 "\t<path> is the path on the filesystem to export.\n"
71                 "\t<comment> is the optional comment for the new share.\n"
72                 "\t<acl> is an optional share acl in the format \"DOMAIN%cname:X,DOMAIN%cname:X,....\"\n"
73                 "\t<guest_ok=y> if present sets \"guest ok = yes\" on this usershare.\n"
74                 "\t\t\"X\" represents a permission and can be any one of the characters f, r or d\n"
75                 "\t\twhere \"f\" means full control, \"r\" means read-only, \"d\" means deny access.\n"
76                 "\t\tname may be a domain user or group. For local users use the local server name "
77                 "instead of \"DOMAIN\"\n"
78                 "\t\tThe default acl is \"Everyone:r\" which allows everyone read-only access.\n"
79                 "\tAdd -l or --long to print the info on the newly added share.\n",
80                 chr, chr );
81         return -1;
82 }
83
84 static int net_usershare_delete_usage(struct net_context *c, int argc, const char **argv)
85 {
86         d_printf(
87                 "net usershare delete <sharename>\n"
88                 "\tdeletes the specified share name for this user.\n");
89         return -1;
90 }
91
92 static int net_usershare_info_usage(struct net_context *c, int argc, const char **argv)
93 {
94         d_printf(
95                 "net usershare info [-l|--long] [wildcard sharename]\n"
96                 "\tPrints out the path, comment and acl elements of shares that match the wildcard.\n"
97                 "\tBy default only gives info on shares owned by the current user\n"
98                 "\tAdd -l or --long to apply this to all shares\n"
99                 "\tOmit the sharename or use a wildcard of '*' to see all shares\n");
100         return -1;
101 }
102
103 static int net_usershare_list_usage(struct net_context *c, int argc, const char **argv)
104 {
105         d_printf(
106                 "net usershare list [-l|--long] [wildcard sharename]\n"
107                 "\tLists the names of all shares that match the wildcard.\n"
108                 "\tBy default only lists shares owned by the current user\n"
109                 "\tAdd -l or --long to apply this to all shares\n"
110                 "\tOmit the sharename or use a wildcard of '*' to see all shares\n");
111         return -1;
112 }
113
114 int net_usershare_usage(struct net_context *c, int argc, const char **argv)
115 {
116         d_printf("net usershare add <sharename> <path> [<comment>] [<acl>] [<guest_ok=[y|n]>] to "
117                                 "add or change a user defined share.\n"
118                 "net usershare delete <sharename> to delete a user defined share.\n"
119                 "net usershare info [-l|--long] [wildcard sharename] to print info about a user defined share.\n"
120                 "net usershare list [-l|--long] [wildcard sharename] to list user defined shares.\n"
121                 "net usershare help\n"
122                 "\nType \"net usershare help <option>\" to get more information on that option\n\n");
123
124         net_common_flags_usage(c, argc, argv);
125         return -1;
126 }
127
128 /***************************************************************************
129 ***************************************************************************/
130
131 static char *get_basepath(TALLOC_CTX *ctx)
132 {
133         char *basepath = talloc_strdup(ctx, lp_usershare_path());
134
135         if (!basepath) {
136                 return NULL;
137         }
138         if ((basepath[0] != '\0') && (basepath[strlen(basepath)-1] == '/')) {
139                 basepath[strlen(basepath)-1] = '\0';
140         }
141         return basepath;
142 }
143
144 /***************************************************************************
145  Delete a single userlevel share.
146 ***************************************************************************/
147
148 static int net_usershare_delete(struct net_context *c, int argc, const char **argv)
149 {
150         char *us_path;
151         char *sharename;
152
153         if (argc != 1 || c->display_usage) {
154                 return net_usershare_delete_usage(c, argc, argv);
155         }
156
157         if ((sharename = strlower_talloc(talloc_tos(), argv[0])) == NULL) {
158                 d_fprintf(stderr, "strlower_talloc failed\n");
159                 return -1;
160         }
161
162         if (!validate_net_name(sharename, INVALID_SHARENAME_CHARS, strlen(sharename))) {
163                 d_fprintf(stderr, "net usershare delete: share name %s contains "
164                         "invalid characters (any of %s)\n",
165                         sharename, INVALID_SHARENAME_CHARS);
166                 TALLOC_FREE(sharename);
167                 return -1;
168         }
169
170         us_path = talloc_asprintf(talloc_tos(),
171                                 "%s/%s",
172                                 lp_usershare_path(),
173                                 sharename);
174         if (!us_path) {
175                 TALLOC_FREE(sharename);
176                 return -1;
177         }
178
179         if (unlink(us_path) != 0) {
180                 d_fprintf(stderr, "net usershare delete: unable to remove usershare %s. "
181                         "Error was %s\n",
182                         us_path, strerror(errno));
183                 TALLOC_FREE(sharename);
184                 return -1;
185         }
186         TALLOC_FREE(sharename);
187         return 0;
188 }
189
190 /***************************************************************************
191  Data structures to handle a list of usershare files.
192 ***************************************************************************/
193
194 struct file_list {
195         struct file_list *next, *prev;
196         const char *pathname;
197 };
198
199 static struct file_list *flist;
200
201 /***************************************************************************
202 ***************************************************************************/
203
204 static int get_share_list(TALLOC_CTX *ctx, const char *wcard, bool only_ours)
205 {
206         SMB_STRUCT_DIR *dp;
207         SMB_STRUCT_DIRENT *de;
208         uid_t myuid = geteuid();
209         struct file_list *fl = NULL;
210         char *basepath = get_basepath(ctx);
211
212         if (!basepath) {
213                 return -1;
214         }
215         dp = sys_opendir(basepath);
216         if (!dp) {
217                 d_fprintf(stderr, "get_share_list: cannot open usershare directory %s. Error %s\n",
218                         basepath, strerror(errno) );
219                 return -1;
220         }
221
222         while((de = sys_readdir(dp)) != 0) {
223                 SMB_STRUCT_STAT sbuf;
224                 char *path;
225                 const char *n = de->d_name;
226
227                 /* Ignore . and .. */
228                 if (*n == '.') {
229                         if ((n[1] == '\0') || (n[1] == '.' && n[2] == '\0')) {
230                                 continue;
231                         }
232                 }
233
234                 if (!validate_net_name(n, INVALID_SHARENAME_CHARS, strlen(n))) {
235                         d_fprintf(stderr, "get_share_list: ignoring bad share name %s\n",n);
236                         continue;
237                 }
238                 path = talloc_asprintf(ctx,
239                                         "%s/%s",
240                                         basepath,
241                                         n);
242                 if (!path) {
243                         sys_closedir(dp);
244                         return -1;
245                 }
246
247                 if (sys_lstat(path, &sbuf) != 0) {
248                         d_fprintf(stderr, "get_share_list: can't lstat file %s. Error was %s\n",
249                                 path, strerror(errno) );
250                         continue;
251                 }
252
253                 if (!S_ISREG(sbuf.st_ex_mode)) {
254                         d_fprintf(stderr, "get_share_list: file %s is not a regular file. Ignoring.\n",
255                                 path );
256                         continue;
257                 }
258
259                 if (only_ours && sbuf.st_ex_uid != myuid) {
260                         continue;
261                 }
262
263                 if (!unix_wild_match(wcard, n)) {
264                         continue;
265                 }
266
267                 /* (Finally) - add to list. */
268                 fl = TALLOC_P(ctx, struct file_list);
269                 if (!fl) {
270                         sys_closedir(dp);
271                         return -1;
272                 }
273                 fl->pathname = talloc_strdup(ctx, n);
274                 if (!fl->pathname) {
275                         sys_closedir(dp);
276                         return -1;
277                 }
278
279                 DLIST_ADD(flist, fl);
280         }
281
282         sys_closedir(dp);
283         return 0;
284 }
285
286 enum us_priv_op { US_LIST_OP, US_INFO_OP};
287
288 struct us_priv_info {
289         TALLOC_CTX *ctx;
290         enum us_priv_op op;
291         struct net_context *c;
292 };
293
294 /***************************************************************************
295  Call a function for every share on the list.
296 ***************************************************************************/
297
298 static int process_share_list(int (*fn)(struct file_list *, void *), void *priv)
299 {
300         struct file_list *fl;
301         int ret = 0;
302
303         for (fl = flist; fl; fl = fl->next) {
304                 ret = (*fn)(fl, priv);
305         }
306
307         return ret;
308 }
309
310 /***************************************************************************
311  Info function.
312 ***************************************************************************/
313
314 static int info_fn(struct file_list *fl, void *priv)
315 {
316         SMB_STRUCT_STAT sbuf;
317         char **lines = NULL;
318         struct us_priv_info *pi = (struct us_priv_info *)priv;
319         TALLOC_CTX *ctx = pi->ctx;
320         struct net_context *c = pi->c;
321         int fd = -1;
322         int numlines = 0;
323         SEC_DESC *psd = NULL;
324         char *basepath;
325         char *sharepath = NULL;
326         char *comment = NULL;
327         char *acl_str;
328         int num_aces;
329         char sep_str[2];
330         enum usershare_err us_err;
331         bool guest_ok = false;
332
333         sep_str[0] = *lp_winbind_separator();
334         sep_str[1] = '\0';
335
336         basepath = get_basepath(ctx);
337         if (!basepath) {
338                 return -1;
339         }
340         basepath = talloc_asprintf_append(basepath,
341                         "/%s",
342                         fl->pathname);
343         if (!basepath) {
344                 return -1;
345         }
346
347 #ifdef O_NOFOLLOW
348         fd = sys_open(basepath, O_RDONLY|O_NOFOLLOW, 0);
349 #else
350         fd = sys_open(basepath, O_RDONLY, 0);
351 #endif
352
353         if (fd == -1) {
354                 d_fprintf(stderr, "info_fn: unable to open %s. %s\n",
355                         basepath, strerror(errno) );
356                 return -1;
357         }
358
359         /* Paranoia... */
360         if (sys_fstat(fd, &sbuf) != 0) {
361                 d_fprintf(stderr, "info_fn: can't fstat file %s. Error was %s\n",
362                         basepath, strerror(errno) );
363                 close(fd);
364                 return -1;
365         }
366
367         if (!S_ISREG(sbuf.st_ex_mode)) {
368                 d_fprintf(stderr, "info_fn: file %s is not a regular file. Ignoring.\n",
369                         basepath );
370                 close(fd);
371                 return -1;
372         }
373
374         lines = fd_lines_load(fd, &numlines, 10240, NULL);
375         close(fd);
376
377         if (lines == NULL) {
378                 return -1;
379         }
380
381         /* Ensure it's well formed. */
382         us_err = parse_usershare_file(ctx, &sbuf, fl->pathname, -1, lines, numlines,
383                                 &sharepath,
384                                 &comment,
385                                 &psd,
386                                 &guest_ok);
387
388         TALLOC_FREE(lines);
389
390         if (us_err != USERSHARE_OK) {
391                 d_fprintf(stderr, "info_fn: file %s is not a well formed usershare file.\n",
392                         basepath );
393                 d_fprintf(stderr, "info_fn: Error was %s.\n",
394                         get_us_error_code(us_err) );
395                 return -1;
396         }
397
398         acl_str = talloc_strdup(ctx, "usershare_acl=");
399         if (!acl_str) {
400                 return -1;
401         }
402
403         for (num_aces = 0; num_aces < psd->dacl->num_aces; num_aces++) {
404                 const char *domain;
405                 const char *name;
406                 NTSTATUS ntstatus;
407
408                 ntstatus = net_lookup_name_from_sid(c, ctx,
409                                                     &psd->dacl->aces[num_aces].trustee,
410                                                     &domain, &name);
411
412                 if (NT_STATUS_IS_OK(ntstatus)) {
413                         if (domain && *domain) {
414                                 acl_str = talloc_asprintf_append(acl_str,
415                                                 "%s%s",
416                                                 domain,
417                                                 sep_str);
418                                 if (!acl_str) {
419                                         return -1;
420                                 }
421                         }
422                         acl_str = talloc_asprintf_append(acl_str,
423                                                 "%s",
424                                                 name);
425                         if (!acl_str) {
426                                 return -1;
427                         }
428
429                 } else {
430                         fstring sidstr;
431                         sid_to_fstring(sidstr,
432                                        &psd->dacl->aces[num_aces].trustee);
433                         acl_str = talloc_asprintf_append(acl_str,
434                                                 "%s",
435                                                 sidstr);
436                         if (!acl_str) {
437                                 return -1;
438                         }
439                 }
440                 acl_str = talloc_asprintf_append(acl_str, ":");
441                 if (!acl_str) {
442                         return -1;
443                 }
444
445                 if (psd->dacl->aces[num_aces].type == SEC_ACE_TYPE_ACCESS_DENIED) {
446                         acl_str = talloc_asprintf_append(acl_str, "D,");
447                         if (!acl_str) {
448                                 return -1;
449                         }
450                 } else {
451                         if (psd->dacl->aces[num_aces].access_mask & GENERIC_ALL_ACCESS) {
452                                 acl_str = talloc_asprintf_append(acl_str, "F,");
453                         } else {
454                                 acl_str = talloc_asprintf_append(acl_str, "R,");
455                         }
456                         if (!acl_str) {
457                                 return -1;
458                         }
459                 }
460         }
461
462         if (pi->op == US_INFO_OP) {
463                 d_printf("[%s]\n", fl->pathname );
464                 d_printf("path=%s\n", sharepath );
465                 d_printf("comment=%s\n", comment);
466                 d_printf("%s\n", acl_str);
467                 d_printf("guest_ok=%c\n\n", guest_ok ? 'y' : 'n');
468         } else if (pi->op == US_LIST_OP) {
469                 d_printf("%s\n", fl->pathname);
470         }
471
472         return 0;
473 }
474
475 /***************************************************************************
476  Print out info (internal detail) on userlevel shares.
477 ***************************************************************************/
478
479 static int net_usershare_info(struct net_context *c, int argc, const char **argv)
480 {
481         fstring wcard;
482         bool only_ours = true;
483         int ret = -1;
484         struct us_priv_info pi;
485         TALLOC_CTX *ctx;
486
487         fstrcpy(wcard, "*");
488
489         if (c->display_usage)
490                 return net_usershare_info_usage(c, argc, argv);
491
492         if (c->opt_long_list_entries) {
493                 only_ours = false;
494         }
495
496         switch (argc) {
497                 case 0:
498                         break;
499                 case 1:
500                         fstrcpy(wcard, argv[0]);
501                         break;
502                 default:
503                         return net_usershare_info_usage(c, argc, argv);
504         }
505
506         strlower_m(wcard);
507
508         ctx = talloc_init("share_info");
509         ret = get_share_list(ctx, wcard, only_ours);
510         if (ret) {
511                 return ret;
512         }
513
514         pi.ctx = ctx;
515         pi.op = US_INFO_OP;
516         pi.c = c;
517
518         ret = process_share_list(info_fn, &pi);
519         talloc_destroy(ctx);
520         return ret;
521 }
522
523 /***************************************************************************
524  Count the current total number of usershares.
525 ***************************************************************************/
526
527 static int count_num_usershares(void)
528 {
529         SMB_STRUCT_DIR *dp;
530         SMB_STRUCT_DIRENT *de;
531         int num_usershares = 0;
532         TALLOC_CTX *ctx = talloc_tos();
533         char *basepath = get_basepath(ctx);
534
535         if (!basepath) {
536                 return -1;
537         }
538
539         dp = sys_opendir(basepath);
540         if (!dp) {
541                 d_fprintf(stderr, "count_num_usershares: cannot open usershare directory %s. Error %s\n",
542                         basepath, strerror(errno) );
543                 return -1;
544         }
545
546         while((de = sys_readdir(dp)) != 0) {
547                 SMB_STRUCT_STAT sbuf;
548                 char *path;
549                 const char *n = de->d_name;
550
551                 /* Ignore . and .. */
552                 if (*n == '.') {
553                         if ((n[1] == '\0') || (n[1] == '.' && n[2] == '\0')) {
554                                 continue;
555                         }
556                 }
557
558                 if (!validate_net_name(n, INVALID_SHARENAME_CHARS, strlen(n))) {
559                         d_fprintf(stderr, "count_num_usershares: ignoring bad share name %s\n",n);
560                         continue;
561                 }
562                 path = talloc_asprintf(ctx,
563                                 "%s/%s",
564                                 basepath,
565                                 n);
566                 if (!path) {
567                         sys_closedir(dp);
568                         return -1;
569                 }
570
571                 if (sys_lstat(path, &sbuf) != 0) {
572                         d_fprintf(stderr, "count_num_usershares: can't lstat file %s. Error was %s\n",
573                                 path, strerror(errno) );
574                         continue;
575                 }
576
577                 if (!S_ISREG(sbuf.st_ex_mode)) {
578                         d_fprintf(stderr, "count_num_usershares: file %s is not a regular file. Ignoring.\n",
579                                 path );
580                         continue;
581                 }
582                 num_usershares++;
583         }
584
585         sys_closedir(dp);
586         return num_usershares;
587 }
588
589 /***************************************************************************
590  Add a single userlevel share.
591 ***************************************************************************/
592
593 static int net_usershare_add(struct net_context *c, int argc, const char **argv)
594 {
595         TALLOC_CTX *ctx = talloc_stackframe();
596         SMB_STRUCT_STAT sbuf;
597         SMB_STRUCT_STAT lsbuf;
598         char *sharename;
599         char *full_path;
600         char *full_path_tmp;
601         const char *us_path;
602         const char *us_comment;
603         const char *arg_acl;
604         char *us_acl;
605         char *file_img;
606         int num_aces = 0;
607         int i;
608         int tmpfd;
609         const char *pacl;
610         size_t to_write;
611         uid_t myeuid = geteuid();
612         bool guest_ok = false;
613         int num_usershares;
614
615         us_comment = "";
616         arg_acl = "S-1-1-0:R";
617
618         if (c->display_usage)
619                 return net_usershare_add_usage(c, argc, argv);
620
621         switch (argc) {
622                 case 0:
623                 case 1:
624                 default:
625                         return net_usershare_add_usage(c, argc, argv);
626                 case 2:
627                         sharename = strlower_talloc(ctx, argv[0]);
628                         us_path = argv[1];
629                         break;
630                 case 3:
631                         sharename = strlower_talloc(ctx, argv[0]);
632                         us_path = argv[1];
633                         us_comment = argv[2];
634                         break;
635                 case 4:
636                         sharename = strlower_talloc(ctx, argv[0]);
637                         us_path = argv[1];
638                         us_comment = argv[2];
639                         arg_acl = argv[3];
640                         break;
641                 case 5:
642                         sharename = strlower_talloc(ctx, argv[0]);
643                         us_path = argv[1];
644                         us_comment = argv[2];
645                         arg_acl = argv[3];
646                         if (strlen(arg_acl) == 0) {
647                                 arg_acl = "S-1-1-0:R";
648                         }
649                         if (!strnequal(argv[4], "guest_ok=", 9)) {
650                                 TALLOC_FREE(ctx);
651                                 return net_usershare_add_usage(c, argc, argv);
652                         }
653                         switch (argv[4][9]) {
654                                 case 'y':
655                                 case 'Y':
656                                         guest_ok = true;
657                                         break;
658                                 case 'n':
659                                 case 'N':
660                                         guest_ok = false;
661                                         break;
662                                 default:
663                                         TALLOC_FREE(ctx);
664                                         return net_usershare_add_usage(c, argc, argv);
665                         }
666                         break;
667         }
668
669         /* Ensure we're under the "usershare max shares" number. Advisory only. */
670         num_usershares = count_num_usershares();
671         if (num_usershares >= lp_usershare_max_shares()) {
672                 d_fprintf(stderr, "net usershare add: maximum number of allowed usershares (%d) reached\n",
673                         lp_usershare_max_shares() );
674                 TALLOC_FREE(ctx);
675                 return -1;
676         }
677
678         if (!validate_net_name(sharename, INVALID_SHARENAME_CHARS, strlen(sharename))) {
679                 d_fprintf(stderr, "net usershare add: share name %s contains "
680                         "invalid characters (any of %s)\n",
681                         sharename, INVALID_SHARENAME_CHARS);
682                 TALLOC_FREE(ctx);
683                 return -1;
684         }
685
686         /* Disallow shares the same as users. */
687         if (getpwnam(sharename)) {
688                 d_fprintf(stderr, "net usershare add: share name %s is already a valid system user name\n",
689                         sharename );
690                 TALLOC_FREE(ctx);
691                 return -1;
692         }
693
694         /* Construct the full path for the usershare file. */
695         full_path = get_basepath(ctx);
696         if (!full_path) {
697                 TALLOC_FREE(ctx);
698                 return -1;
699         }
700         full_path_tmp = talloc_asprintf(ctx,
701                         "%s/:tmpXXXXXX",
702                         full_path);
703         if (!full_path_tmp) {
704                 TALLOC_FREE(ctx);
705                 return -1;
706         }
707
708         full_path = talloc_asprintf_append(full_path,
709                                         "/%s",
710                                         sharename);
711         if (!full_path) {
712                 TALLOC_FREE(ctx);
713                 return -1;
714         }
715
716         /* The path *must* be absolute. */
717         if (us_path[0] != '/') {
718                 d_fprintf(stderr,"net usershare add: path %s is not an absolute path.\n",
719                         us_path);
720                 TALLOC_FREE(ctx);
721                 return -1;
722         }
723
724         /* Check the directory to be shared exists. */
725         if (sys_stat(us_path, &sbuf) != 0) {
726                 d_fprintf(stderr, "net usershare add: cannot stat path %s to ensure "
727                         "this is a directory. Error was %s\n",
728                         us_path, strerror(errno) );
729                 TALLOC_FREE(ctx);
730                 return -1;
731         }
732
733         if (!S_ISDIR(sbuf.st_ex_mode)) {
734                 d_fprintf(stderr, "net usershare add: path %s is not a directory.\n",
735                         us_path );
736                 TALLOC_FREE(ctx);
737                 return -1;
738         }
739
740         /* If we're not root, check if we're restricted to sharing out directories
741            that we own only. */
742
743         if ((myeuid != 0) && lp_usershare_owner_only() && (myeuid != sbuf.st_ex_uid)) {
744                 d_fprintf(stderr, "net usershare add: cannot share path %s as "
745                         "we are restricted to only sharing directories we own.\n"
746                         "\tAsk the administrator to add the line \"usershare owner only = false\" \n"
747                         "\tto the [global] section of the smb.conf to allow this.\n",
748                         us_path );
749                 TALLOC_FREE(ctx);
750                 return -1;
751         }
752
753         /* No validation needed on comment. Now go through and validate the
754            acl string. Convert names to SID's as needed. Then run it through
755            parse_usershare_acl to ensure it's valid. */
756
757         /* Start off the string we'll append to. */
758         us_acl = talloc_strdup(ctx, "");
759         if (!us_acl) {
760                 TALLOC_FREE(ctx);
761                 return -1;
762         }
763
764         pacl = arg_acl;
765         num_aces = 1;
766
767         /* Add the number of ',' characters to get the number of aces. */
768         num_aces += count_chars(pacl,',');
769
770         for (i = 0; i < num_aces; i++) {
771                 DOM_SID sid;
772                 const char *pcolon = strchr_m(pacl, ':');
773                 const char *name;
774
775                 if (pcolon == NULL) {
776                         d_fprintf(stderr, "net usershare add: malformed acl %s (missing ':').\n",
777                                 pacl );
778                         TALLOC_FREE(ctx);
779                         return -1;
780                 }
781
782                 switch(pcolon[1]) {
783                         case 'f':
784                         case 'F':
785                         case 'd':
786                         case 'r':
787                         case 'R':
788                                 break;
789                         default:
790                                 d_fprintf(stderr, "net usershare add: malformed acl %s "
791                                         "(access control must be 'r', 'f', or 'd')\n",
792                                         pacl );
793                                 TALLOC_FREE(ctx);
794                                 return -1;
795                 }
796
797                 if (pcolon[2] != ',' && pcolon[2] != '\0') {
798                         d_fprintf(stderr, "net usershare add: malformed terminating character for acl %s\n",
799                                 pacl );
800                         TALLOC_FREE(ctx);
801                         return -1;
802                 }
803
804                 /* Get the name */
805                 if ((name = talloc_strndup(ctx, pacl, pcolon - pacl)) == NULL) {
806                         d_fprintf(stderr, "talloc_strndup failed\n");
807                         TALLOC_FREE(ctx);
808                         return -1;
809                 }
810                 if (!string_to_sid(&sid, name)) {
811                         /* Convert to a SID */
812                         NTSTATUS ntstatus = net_lookup_sid_from_name(c, ctx, name, &sid);
813                         if (!NT_STATUS_IS_OK(ntstatus)) {
814                                 d_fprintf(stderr, "net usershare add: cannot convert name \"%s\" to a SID. %s.",
815                                         name, get_friendly_nt_error_msg(ntstatus) );
816                                 if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_CONNECTION_REFUSED)) {
817                                         d_fprintf(stderr,  " Maybe smbd is not running.\n");
818                                 } else {
819                                         d_fprintf(stderr, "\n");
820                                 }
821                                 TALLOC_FREE(ctx);
822                                 return -1;
823                         }
824                 }
825                 us_acl = talloc_asprintf_append(
826                         us_acl, "%s:%c,", sid_string_tos(&sid), pcolon[1]);
827
828                 /* Move to the next ACL entry. */
829                 if (pcolon[2] == ',') {
830                         pacl = &pcolon[3];
831                 }
832         }
833
834         /* Remove the last ',' */
835         us_acl[strlen(us_acl)-1] = '\0';
836
837         if (guest_ok && !lp_usershare_allow_guests()) {
838                 d_fprintf(stderr, "net usershare add: guest_ok=y requested "
839                         "but the \"usershare allow guests\" parameter is not enabled "
840                         "by this server.\n");
841                 TALLOC_FREE(ctx);
842                 return -1;
843         }
844
845         /* Create a temporary filename for this share. */
846         tmpfd = mkstemp(full_path_tmp);
847
848         if (tmpfd == -1) {
849                 d_fprintf(stderr, "net usershare add: cannot create tmp file %s\n",
850                                 full_path_tmp );
851                 TALLOC_FREE(ctx);
852                 return -1;
853         }
854
855         /* Ensure we opened the file we thought we did. */
856         if (sys_lstat(full_path_tmp, &lsbuf) != 0) {
857                 d_fprintf(stderr, "net usershare add: cannot lstat tmp file %s\n",
858                                 full_path_tmp );
859                 TALLOC_FREE(ctx);
860                 return -1;
861         }
862
863         /* Check this is the same as the file we opened. */
864         if (sys_fstat(tmpfd, &sbuf) != 0) {
865                 d_fprintf(stderr, "net usershare add: cannot fstat tmp file %s\n",
866                                 full_path_tmp );
867                 TALLOC_FREE(ctx);
868                 return -1;
869         }
870
871         if (!S_ISREG(sbuf.st_ex_mode) || sbuf.st_ex_dev != lsbuf.st_ex_dev || sbuf.st_ex_ino != lsbuf.st_ex_ino) {
872                 d_fprintf(stderr, "net usershare add: tmp file %s is not a regular file ?\n",
873                                 full_path_tmp );
874                 TALLOC_FREE(ctx);
875                 return -1;
876         }
877
878         if (fchmod(tmpfd, 0644) == -1) {
879                 d_fprintf(stderr, "net usershare add: failed to fchmod tmp file %s to 0644n",
880                                 full_path_tmp );
881                 TALLOC_FREE(ctx);
882                 return -1;
883         }
884
885         /* Create the in-memory image of the file. */
886         file_img = talloc_strdup(ctx, "#VERSION 2\npath=");
887         file_img = talloc_asprintf_append(file_img, "%s\ncomment=%s\nusershare_acl=%s\nguest_ok=%c\n",
888                         us_path, us_comment, us_acl, guest_ok ? 'y' : 'n');
889
890         to_write = strlen(file_img);
891
892         if (write(tmpfd, file_img, to_write) != to_write) {
893                 d_fprintf(stderr, "net usershare add: failed to write %u bytes to file %s. Error was %s\n",
894                         (unsigned int)to_write, full_path_tmp, strerror(errno));
895                 unlink(full_path_tmp);
896                 TALLOC_FREE(ctx);
897                 return -1;
898         }
899
900         /* Attempt to replace any existing share by this name. */
901         if (rename(full_path_tmp, full_path) != 0) {
902                 unlink(full_path_tmp);
903                 d_fprintf(stderr, "net usershare add: failed to add share %s. Error was %s\n",
904                         sharename, strerror(errno));
905                 TALLOC_FREE(ctx);
906                 close(tmpfd);
907                 return -1;
908         }
909
910         close(tmpfd);
911
912         if (c->opt_long_list_entries) {
913                 const char *my_argv[2];
914                 my_argv[0] = sharename;
915                 my_argv[1] = NULL;
916                 net_usershare_info(c, 1, my_argv);
917         }
918
919         TALLOC_FREE(ctx);
920         return 0;
921 }
922
923 #if 0
924 /***************************************************************************
925  List function.
926 ***************************************************************************/
927
928 static int list_fn(struct file_list *fl, void *priv)
929 {
930         d_printf("%s\n", fl->pathname);
931         return 0;
932 }
933 #endif
934
935 /***************************************************************************
936  List userlevel shares.
937 ***************************************************************************/
938
939 static int net_usershare_list(struct net_context *c, int argc,
940                               const char **argv)
941 {
942         fstring wcard;
943         bool only_ours = true;
944         int ret = -1;
945         struct us_priv_info pi;
946         TALLOC_CTX *ctx;
947
948         fstrcpy(wcard, "*");
949
950         if (c->display_usage)
951                 return net_usershare_list_usage(c, argc, argv);
952
953         if (c->opt_long_list_entries) {
954                 only_ours = false;
955         }
956
957         switch (argc) {
958                 case 0:
959                         break;
960                 case 1:
961                         fstrcpy(wcard, argv[0]);
962                         break;
963                 default:
964                         return net_usershare_list_usage(c, argc, argv);
965         }
966
967         strlower_m(wcard);
968
969         ctx = talloc_init("share_list");
970         ret = get_share_list(ctx, wcard, only_ours);
971         if (ret) {
972                 return ret;
973         }
974
975         pi.ctx = ctx;
976         pi.op = US_LIST_OP;
977
978         ret = process_share_list(info_fn, &pi);
979         talloc_destroy(ctx);
980         return ret;
981 }
982
983 /***************************************************************************
984  Entry-point for all the USERSHARE functions.
985 ***************************************************************************/
986
987 int net_usershare(struct net_context *c, int argc, const char **argv)
988 {
989         SMB_STRUCT_DIR *dp;
990
991         struct functable func[] = {
992                 {
993                         "add",
994                         net_usershare_add,
995                         NET_TRANSPORT_LOCAL,
996                         "Add/modify user defined share",
997                         "net usershare add\n"
998                         "    Add/modify user defined share"
999                 },
1000                 {
1001                         "delete",
1002                         net_usershare_delete,
1003                         NET_TRANSPORT_LOCAL,
1004                         "Delete user defined share",
1005                         "net usershare delete\n"
1006                         "    Delete user defined share"
1007                 },
1008                 {
1009                         "info",
1010                         net_usershare_info,
1011                         NET_TRANSPORT_LOCAL,
1012                         "Display information about a user defined share",
1013                         "net usershare info\n"
1014                         "    Display information about a user defined share"
1015                 },
1016                 {
1017                         "list",
1018                         net_usershare_list,
1019                         NET_TRANSPORT_LOCAL,
1020                         "List user defined shares",
1021                         "net usershare list\n"
1022                         "    List user defined shares"
1023                 },
1024                 {NULL, NULL, 0, NULL, NULL}
1025         };
1026
1027         if (lp_usershare_max_shares() == 0) {
1028                 d_fprintf(stderr, "net usershare: usershares are currently disabled\n");
1029                 return -1;
1030         }
1031
1032         dp = sys_opendir(lp_usershare_path());
1033         if (!dp) {
1034                 int err = errno;
1035                 d_fprintf(stderr, "net usershare: cannot open usershare directory %s. Error %s\n",
1036                         lp_usershare_path(), strerror(err) );
1037                 if (err == EACCES) {
1038                         d_fprintf(stderr, "You do not have permission to create a usershare. Ask your "
1039                                 "administrator to grant you permissions to create a share.\n");
1040                 } else if (err == ENOENT) {
1041                         d_fprintf(stderr, "Please ask your system administrator to "
1042                                 "enable user sharing.\n");
1043                 }
1044                 return -1;
1045         }
1046         sys_closedir(dp);
1047
1048         return net_run_function(c, argc, argv, "net usershare", func);
1049 }