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