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