]> git.proxmox.com Git - mirror_zfs.git/blob - lib/libshare/smb.c
Fedora 28: Fix misc bounds check compiler warnings
[mirror_zfs.git] / lib / libshare / smb.c
1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2011,2012 Turbo Fredriksson <turbo@bayour.com>, based on nfs.c
25 * by Gunnar Beutner
26 *
27 * This is an addition to the zfs device driver to add, modify and remove SMB
28 * shares using the 'net share' command that comes with Samba.
29 *
30 * TESTING
31 * Make sure that samba listens to 'localhost' (127.0.0.1) and that the options
32 * 'usershare max shares' and 'usershare owner only' have been rewied/set
33 * accordingly (see zfs(8) for information).
34 *
35 * Once configuration in samba have been done, test that this
36 * works with the following three commands (in this case, my ZFS
37 * filesystem is called 'share/Test1'):
38 *
39 * (root)# net -U root -S 127.0.0.1 usershare add Test1 /share/Test1 \
40 * "Comment: /share/Test1" "Everyone:F"
41 * (root)# net usershare list | grep -i test
42 * (root)# net -U root -S 127.0.0.1 usershare delete Test1
43 *
44 * The first command will create a user share that gives everyone full access.
45 * To limit the access below that, use normal UNIX commands (chmod, chown etc).
46 */
47
48 #include <time.h>
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <string.h>
52 #include <strings.h>
53 #include <fcntl.h>
54 #include <sys/wait.h>
55 #include <unistd.h>
56 #include <dirent.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <libzfs.h>
60 #include <libshare.h>
61 #include "libshare_impl.h"
62 #include "smb.h"
63
64 static boolean_t smb_available(void);
65
66 static sa_fstype_t *smb_fstype;
67
68 /*
69 * Retrieve the list of SMB shares.
70 */
71 static int
72 smb_retrieve_shares(void)
73 {
74 int rc = SA_OK;
75 char file_path[PATH_MAX], line[512], *token, *key, *value;
76 char *dup_value = NULL, *path = NULL, *comment = NULL, *name = NULL;
77 char *guest_ok = NULL;
78 DIR *shares_dir;
79 FILE *share_file_fp = NULL;
80 struct dirent *directory;
81 struct stat eStat;
82 smb_share_t *shares, *new_shares = NULL;
83
84 /* opendir(), stat() */
85 shares_dir = opendir(SHARE_DIR);
86 if (shares_dir == NULL)
87 return (SA_SYSTEM_ERR);
88
89 /* Go through the directory, looking for shares */
90 while ((directory = readdir(shares_dir))) {
91 if (directory->d_name[0] == '.')
92 continue;
93
94 snprintf(file_path, sizeof (file_path),
95 "%s/%s", SHARE_DIR, directory->d_name);
96
97 if (stat(file_path, &eStat) == -1) {
98 rc = SA_SYSTEM_ERR;
99 goto out;
100 }
101
102 if (!S_ISREG(eStat.st_mode))
103 continue;
104
105 if ((share_file_fp = fopen(file_path, "r")) == NULL) {
106 rc = SA_SYSTEM_ERR;
107 goto out;
108 }
109
110 name = strdup(directory->d_name);
111 if (name == NULL) {
112 rc = SA_NO_MEMORY;
113 goto out;
114 }
115
116 while (fgets(line, sizeof (line), share_file_fp)) {
117 if (line[0] == '#')
118 continue;
119
120 /* Trim trailing new-line character(s). */
121 while (line[strlen(line) - 1] == '\r' ||
122 line[strlen(line) - 1] == '\n')
123 line[strlen(line) - 1] = '\0';
124
125 /* Split the line in two, separated by '=' */
126 token = strchr(line, '=');
127 if (token == NULL)
128 continue;
129
130 key = line;
131 value = token + 1;
132 *token = '\0';
133
134 dup_value = strdup(value);
135 if (dup_value == NULL) {
136 rc = SA_NO_MEMORY;
137 goto out;
138 }
139
140 if (strcmp(key, "path") == 0) {
141 free(path);
142 path = dup_value;
143 } else if (strcmp(key, "comment") == 0) {
144 free(comment);
145 comment = dup_value;
146 } else if (strcmp(key, "guest_ok") == 0) {
147 free(guest_ok);
148 guest_ok = dup_value;
149 } else
150 free(dup_value);
151
152 dup_value = NULL;
153
154 if (path == NULL || comment == NULL || guest_ok == NULL)
155 continue; /* Incomplete share definition */
156 else {
157 shares = (smb_share_t *)
158 malloc(sizeof (smb_share_t));
159 if (shares == NULL) {
160 rc = SA_NO_MEMORY;
161 goto out;
162 }
163
164 (void) strlcpy(shares->name, name,
165 sizeof (shares->name));
166
167 (void) strlcpy(shares->path, path,
168 sizeof (shares->path));
169
170 (void) strlcpy(shares->comment, comment,
171 sizeof (shares->comment));
172
173 shares->guest_ok = atoi(guest_ok);
174
175 shares->next = new_shares;
176 new_shares = shares;
177
178 free(path);
179 free(comment);
180 free(guest_ok);
181
182 path = NULL;
183 comment = NULL;
184 guest_ok = NULL;
185 }
186 }
187
188 out:
189 if (share_file_fp != NULL) {
190 fclose(share_file_fp);
191 share_file_fp = NULL;
192 }
193
194 free(name);
195 free(path);
196 free(comment);
197 free(guest_ok);
198
199 name = NULL;
200 path = NULL;
201 comment = NULL;
202 guest_ok = NULL;
203 }
204 closedir(shares_dir);
205
206 smb_shares = new_shares;
207
208 return (rc);
209 }
210
211 /*
212 * Used internally by smb_enable_share to enable sharing for a single host.
213 */
214 static int
215 smb_enable_share_one(const char *sharename, const char *sharepath)
216 {
217 char *argv[10], *pos;
218 char name[SMB_NAME_MAX], comment[SMB_COMMENT_MAX];
219 int rc;
220
221 /* Support ZFS share name regexp '[[:alnum:]_-.: ]' */
222 strlcpy(name, sharename, sizeof (name));
223 name [sizeof (name)-1] = '\0';
224
225 pos = name;
226 while (*pos != '\0') {
227 switch (*pos) {
228 case '/':
229 case '-':
230 case ':':
231 case ' ':
232 *pos = '_';
233 }
234
235 ++pos;
236 }
237
238 /*
239 * CMD: net -S NET_CMD_ARG_HOST usershare add Test1 /share/Test1 \
240 * "Comment" "Everyone:F"
241 */
242 snprintf(comment, sizeof (comment), "Comment: %s", sharepath);
243
244 argv[0] = NET_CMD_PATH;
245 argv[1] = (char *)"-S";
246 argv[2] = NET_CMD_ARG_HOST;
247 argv[3] = (char *)"usershare";
248 argv[4] = (char *)"add";
249 argv[5] = (char *)name;
250 argv[6] = (char *)sharepath;
251 argv[7] = (char *)comment;
252 argv[8] = "Everyone:F";
253 argv[9] = NULL;
254
255 rc = libzfs_run_process(argv[0], argv, 0);
256 if (rc < 0)
257 return (SA_SYSTEM_ERR);
258
259 /* Reload the share file */
260 (void) smb_retrieve_shares();
261
262 return (SA_OK);
263 }
264
265 /*
266 * Enables SMB sharing for the specified share.
267 */
268 static int
269 smb_enable_share(sa_share_impl_t impl_share)
270 {
271 char *shareopts;
272
273 if (!smb_available())
274 return (SA_SYSTEM_ERR);
275
276 shareopts = FSINFO(impl_share, smb_fstype)->shareopts;
277 if (shareopts == NULL) /* on/off */
278 return (SA_SYSTEM_ERR);
279
280 if (strcmp(shareopts, "off") == 0)
281 return (SA_OK);
282
283 /* Magic: Enable (i.e., 'create new') share */
284 return (smb_enable_share_one(impl_share->dataset,
285 impl_share->sharepath));
286 }
287
288 /*
289 * Used internally by smb_disable_share to disable sharing for a single host.
290 */
291 static int
292 smb_disable_share_one(const char *sharename)
293 {
294 int rc;
295 char *argv[7];
296
297 /* CMD: net -S NET_CMD_ARG_HOST usershare delete Test1 */
298 argv[0] = NET_CMD_PATH;
299 argv[1] = (char *)"-S";
300 argv[2] = NET_CMD_ARG_HOST;
301 argv[3] = (char *)"usershare";
302 argv[4] = (char *)"delete";
303 argv[5] = strdup(sharename);
304 argv[6] = NULL;
305
306 rc = libzfs_run_process(argv[0], argv, 0);
307 if (rc < 0)
308 return (SA_SYSTEM_ERR);
309 else
310 return (SA_OK);
311 }
312
313 /*
314 * Disables SMB sharing for the specified share.
315 */
316 static int
317 smb_disable_share(sa_share_impl_t impl_share)
318 {
319 smb_share_t *shares = smb_shares;
320
321 if (!smb_available()) {
322 /*
323 * The share can't possibly be active, so nothing
324 * needs to be done to disable it.
325 */
326 return (SA_OK);
327 }
328
329 while (shares != NULL) {
330 if (strcmp(impl_share->sharepath, shares->path) == 0)
331 return (smb_disable_share_one(shares->name));
332
333 shares = shares->next;
334 }
335
336 return (SA_OK);
337 }
338
339 /*
340 * Checks whether the specified SMB share options are syntactically correct.
341 */
342 static int
343 smb_validate_shareopts(const char *shareopts)
344 {
345 /* TODO: Accept 'name' and sec/acl (?) */
346 if ((strcmp(shareopts, "off") == 0) || (strcmp(shareopts, "on") == 0))
347 return (SA_OK);
348
349 return (SA_SYNTAX_ERR);
350 }
351
352 /*
353 * Checks whether a share is currently active.
354 */
355 static boolean_t
356 smb_is_share_active(sa_share_impl_t impl_share)
357 {
358 smb_share_t *iter = smb_shares;
359
360 if (!smb_available())
361 return (B_FALSE);
362
363 /* Retrieve the list of (possible) active shares */
364 smb_retrieve_shares();
365
366 while (iter != NULL) {
367 if (strcmp(impl_share->sharepath, iter->path) == 0)
368 return (B_TRUE);
369
370 iter = iter->next;
371 }
372
373 return (B_FALSE);
374 }
375
376 /*
377 * Called to update a share's options. A share's options might be out of
378 * date if the share was loaded from disk and the "sharesmb" dataset
379 * property has changed in the meantime. This function also takes care
380 * of re-enabling the share if necessary.
381 */
382 static int
383 smb_update_shareopts(sa_share_impl_t impl_share, const char *resource,
384 const char *shareopts)
385 {
386 char *shareopts_dup;
387 boolean_t needs_reshare = B_FALSE;
388 char *old_shareopts;
389
390 if (!impl_share)
391 return (SA_SYSTEM_ERR);
392
393 FSINFO(impl_share, smb_fstype)->active =
394 smb_is_share_active(impl_share);
395
396 old_shareopts = FSINFO(impl_share, smb_fstype)->shareopts;
397
398 if (FSINFO(impl_share, smb_fstype)->active && old_shareopts != NULL &&
399 strcmp(old_shareopts, shareopts) != 0) {
400 needs_reshare = B_TRUE;
401 smb_disable_share(impl_share);
402 }
403
404 shareopts_dup = strdup(shareopts);
405
406 if (shareopts_dup == NULL)
407 return (SA_NO_MEMORY);
408
409 if (old_shareopts != NULL)
410 free(old_shareopts);
411
412 FSINFO(impl_share, smb_fstype)->shareopts = shareopts_dup;
413
414 if (needs_reshare)
415 smb_enable_share(impl_share);
416
417 return (SA_OK);
418 }
419
420 /*
421 * Clears a share's SMB options. Used by libshare to
422 * clean up shares that are about to be free()'d.
423 */
424 static void
425 smb_clear_shareopts(sa_share_impl_t impl_share)
426 {
427 free(FSINFO(impl_share, smb_fstype)->shareopts);
428 FSINFO(impl_share, smb_fstype)->shareopts = NULL;
429 }
430
431 static const sa_share_ops_t smb_shareops = {
432 .enable_share = smb_enable_share,
433 .disable_share = smb_disable_share,
434
435 .validate_shareopts = smb_validate_shareopts,
436 .update_shareopts = smb_update_shareopts,
437 .clear_shareopts = smb_clear_shareopts,
438 };
439
440 /*
441 * Provides a convenient wrapper for determining SMB availability
442 */
443 static boolean_t
444 smb_available(void)
445 {
446 struct stat statbuf;
447
448 if (lstat(SHARE_DIR, &statbuf) != 0 ||
449 !S_ISDIR(statbuf.st_mode))
450 return (B_FALSE);
451
452 if (access(NET_CMD_PATH, F_OK) != 0)
453 return (B_FALSE);
454
455 return (B_TRUE);
456 }
457
458 /*
459 * Initializes the SMB functionality of libshare.
460 */
461 void
462 libshare_smb_init(void)
463 {
464 smb_fstype = register_fstype("smb", &smb_shareops);
465 }