1/* 2 Copyright (c) 2010 Frank Lahm <franklahm@gmail.com> 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13*/ 14 15/*! 16 * @file 17 * Netatalk utility functions 18 */ 19 20#ifdef HAVE_CONFIG_H 21#include "config.h" 22#endif /* HAVE_CONFIG_H */ 23 24#include <unistd.h> 25#include <stdint.h> 26#include <errno.h> 27#include <stdlib.h> 28#include <string.h> 29#include <sys/types.h> 30#include <sys/stat.h> 31#include <fcntl.h> 32#include <dirent.h> 33#include <sys/time.h> 34#include <time.h> 35#include <sys/wait.h> 36#include <libgen.h> 37 38#include <atalk/adouble.h> 39#include <atalk/ea.h> 40#include <atalk/afp.h> 41#include <atalk/logger.h> 42#include <atalk/vfs.h> 43#include <atalk/util.h> 44#include <atalk/unix.h> 45#include <atalk/compat.h> 46#include <atalk/errchk.h> 47 48/* close all FDs >= a specified value */ 49static void closeall(int fd) 50{ 51 int fdlimit = sysconf(_SC_OPEN_MAX); 52 53 while (fd < fdlimit) 54 close(fd++); 55} 56 57/*! 58 * Run command in a child and wait for it to finish 59 */ 60int run_cmd(const char *cmd, char **cmd_argv) 61{ 62 EC_INIT; 63 pid_t pid, wpid; 64 sigset_t sigs, oldsigs; 65 int status = 0; 66 67 sigfillset(&sigs); 68 pthread_sigmask(SIG_SETMASK, &sigs, &oldsigs); 69 70 if ((pid = fork()) < 0) { 71 LOG(log_error, logtype_default, "run_cmd: fork: %s", strerror(errno)); 72 return -1; 73 } 74 75 if (pid == 0) { 76 /* child */ 77 closeall(3); 78 execvp("mv", cmd_argv); 79 } 80 81 /* parent */ 82 while ((wpid = waitpid(pid, &status, 0)) < 0) { 83 if (errno == EINTR) 84 continue; 85 break; 86 } 87 if (wpid != pid) { 88 LOG(log_error, logtype_default, "waitpid(%d): %s", (int)pid, strerror(errno)); 89 EC_FAIL; 90 } 91 92 if (WIFEXITED(status)) 93 status = WEXITSTATUS(status); 94 else if (WIFSIGNALED(status)) 95 status = WTERMSIG(status); 96 97 LOG(log_note, logtype_default, "run_cmd(\"%s\"): status: %d", cmd, status); 98 99EC_CLEANUP: 100 if (status != 0) 101 ret = status; 102 pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); 103 EC_EXIT; 104} 105 106/*! 107 * Daemonize 108 * 109 * Fork, exit parent, setsid(), optionally chdir("/"), optionally close all fds 110 * 111 * returns -1 on failure, but you can't do much except exit in that case 112 * since we may already have forked 113 */ 114int daemonize(int nochdir, int noclose) 115{ 116 switch (fork()) { 117 case 0: 118 break; 119 case -1: 120 return -1; 121 default: 122 _exit(0); 123 } 124 125 if (setsid() < 0) 126 return -1; 127 128 switch (fork()) { 129 case 0: 130 break; 131 case -1: 132 return -1; 133 default: 134 _exit(0); 135 } 136 137 if (!nochdir) 138 chdir("/"); 139 140 if (!noclose) { 141 closeall(0); 142 open("/dev/null",O_RDWR); 143 dup(0); 144 dup(0); 145 } 146 147 return 0; 148} 149 150static uid_t saved_uid = -1; 151 152/* 153 * seteuid(0) and back, if either fails and panic != 0 we PANIC 154 */ 155void become_root(void) 156{ 157 if (getuid() == 0) { 158 saved_uid = geteuid(); 159 if (seteuid(0) != 0) 160 AFP_PANIC("Can't seteuid(0)"); 161 } 162} 163 164void unbecome_root(void) 165{ 166 if (getuid() == 0) { 167 if (saved_uid == -1 || seteuid(saved_uid) < 0) 168 AFP_PANIC("Can't seteuid back"); 169 saved_uid = -1; 170 } 171} 172 173/*! 174 * @brief get cwd in static buffer 175 * 176 * @returns pointer to path or pointer to error messages on error 177 */ 178const char *getcwdpath(void) 179{ 180 static char cwd[MAXPATHLEN + 1]; 181 char *p; 182 183 if ((p = getcwd(cwd, MAXPATHLEN)) != NULL) 184 return p; 185 else 186 return strerror(errno); 187} 188 189/*! 190 * @brief Request absolute path 191 * 192 * @returns Absolute filesystem path to object 193 */ 194const char *fullpathname(const char *name) 195{ 196 static char wd[MAXPATHLEN + 1]; 197 198 if (name[0] == '/') 199 return name; 200 201 if (getcwd(wd , MAXPATHLEN)) { 202 strlcat(wd, "/", MAXPATHLEN); 203 strlcat(wd, name, MAXPATHLEN); 204 } else { 205 strlcpy(wd, name, MAXPATHLEN); 206 } 207 208 return wd; 209} 210 211/*! 212 * Takes a buffer with a path, strips slashs, returns basename 213 * 214 * @param p (rw) path 215 * path may be 216 * "[/][dir/[...]]file" 217 * or 218 * "[/][dir/[...]]dir/[/]" 219 * Result is "file" or "dir" 220 * 221 * @returns pointer to basename in path buffer, buffer is possibly modified 222 */ 223char *stripped_slashes_basename(char *p) 224{ 225 int i = strlen(p) - 1; 226 while (i > 0 && p[i] == '/') 227 p[i--] = 0; 228 return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p); 229} 230 231/********************************************************************************* 232 * chdir(), chmod(), chown(), stat() wrappers taking an additional option. 233 * Currently the only used options are O_NOFOLLOW, used to switch between symlink 234 * behaviour, and O_NETATALK_ACL for ochmod() indicating chmod_acl() shall be 235 * called which does special ACL handling depending on the filesytem 236 *********************************************************************************/ 237 238int ostat(const char *path, struct stat *buf, int options) 239{ 240 if (options & O_NOFOLLOW) 241 return lstat(path, buf); 242 else 243 return stat(path, buf); 244} 245 246int ochown(const char *path, uid_t owner, gid_t group, int options) 247{ 248 if (options & O_NOFOLLOW) 249 return lchown(path, owner, group); 250 else 251 return chown(path, owner, group); 252} 253 254/*! 255 * chmod() wrapper for symlink and ACL handling 256 * 257 * @param path (r) path 258 * @param mode (r) requested mode 259 * @param sb (r) stat() of path or NULL 260 * @param option (r) O_NOFOLLOW | O_NETATALK_ACL 261 * 262 * Options description: 263 * O_NOFOLLOW: don't chmod() symlinks, do nothing, return 0 264 * O_NETATALK_ACL: call chmod_acl() instead of chmod() 265 */ 266int ochmod(char *path, mode_t mode, const struct stat *st, int options) 267{ 268 struct stat sb; 269 270 if (!st) { 271 if (lstat(path, &sb) != 0) 272 return -1; 273 st = &sb; 274 } 275 276 if (options & O_NOFOLLOW) 277 if (S_ISLNK(st->st_mode)) 278 return 0; 279 280 if (options & O_NETATALK_ACL) { 281 return chmod_acl(path, mode); 282 } else { 283 return chmod(path, mode); 284 } 285} 286 287/* 288 * @brief ostat/fsstatat multiplexer 289 * 290 * ostatat mulitplexes ostat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored. 291 * 292 * @param dirfd (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD 293 * @param path (r) pathname 294 * @param st (rw) pointer to struct stat 295 */ 296int ostatat(int dirfd, const char *path, struct stat *st, int options) 297{ 298#ifdef HAVE_ATFUNCS 299 if (dirfd == -1) 300 dirfd = AT_FDCWD; 301 return fstatat(dirfd, path, st, (options & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); 302#else 303 return ostat(path, st, options); 304#endif 305 306 /* DEADC0DE */ 307 return -1; 308} 309 310/*! 311 * @brief symlink safe chdir replacement 312 * 313 * Only chdirs to dir if it doesn't contain symlinks or if symlink checking 314 * is disabled 315 * 316 * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror 317 */ 318int ochdir(const char *dir, int options) 319{ 320 char buf[MAXPATHLEN+1]; 321 char cwd[MAXPATHLEN+1]; 322 char *test; 323 int i; 324 325 if (!(options & O_NOFOLLOW)) 326 return chdir(dir); 327 328 /* 329 dir is a canonical path (without "../" "./" "//" ) 330 but may end with a / 331 */ 332 *cwd = 0; 333 if (*dir != '/') { 334 if (getcwd(cwd, MAXPATHLEN) == NULL) 335 return -1; 336 } 337 if (chdir(dir) != 0) 338 return -1; 339 340 /* 341 * Cases: 342 * chdir request | realpath result | ret 343 * (after getwcwd) | | 344 * ======================================= 345 * /a/b/. | /a/b | 0 346 * /a/b/. | /c | 1 347 * /a/b/. | /c/d/e/f | 1 348 */ 349 if (getcwd(buf, MAXPATHLEN) == NULL) 350 return 1; 351 352 i = 0; 353 if (*cwd) { 354 /* relative path requested, 355 * Same directory? 356 */ 357 for (; cwd[i]; i++) { 358 if (buf[i] != cwd[i]) 359 return 1; 360 } 361 if (buf[i]) { 362 if (buf[i] != '/') 363 return 1; 364 i++; 365 } 366 } 367 368 test = &buf[i]; 369 for (i = 0; test[i]; i++) { 370 if (test[i] != dir[i]) { 371 return 1; 372 } 373 } 374 /* trailing '/' ? */ 375 if (!dir[i]) 376 return 0; 377 378 if (dir[i] != '/') 379 return 1; 380 381 i++; 382 if (dir[i]) 383 return 1; 384 385 return 0; 386} 387 388/*! 389 * Store n random bytes an buf 390 */ 391void randombytes(void *buf, int n) 392{ 393 char *p = (char *)buf; 394 int fd, i; 395 struct timeval tv; 396 397 if ((fd = open("/dev/urandom", O_RDONLY)) != -1) { 398 /* generate from /dev/urandom */ 399 if (read(fd, buf, n) != n) { 400 close(fd); 401 fd = -1; 402 } else { 403 close(fd); 404 /* fd now != -1, so srandom wont be called below */ 405 } 406 } 407 408 if (fd == -1) { 409 gettimeofday(&tv, NULL); 410 srandom((unsigned int)tv.tv_usec); 411 for (i=0 ; i < n ; i++) 412 p[i] = random() & 0xFF; 413 } 414 415 return; 416} 417 418int gmem(gid_t gid, int ngroups, gid_t *groups) 419{ 420 int i; 421 422 for ( i = 0; i < ngroups; i++ ) { 423 if ( groups[ i ] == gid ) { 424 return( 1 ); 425 } 426 } 427 return( 0 ); 428} 429 430/* 431 * realpath() replacement that always allocates storage for returned path 432 */ 433char *realpath_safe(const char *path) 434{ 435 char *resolved_path; 436 437#ifdef REALPATH_TAKES_NULL 438 if ((resolved_path = realpath(path, NULL)) == NULL) { 439 LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path); 440 return NULL; 441 } 442 return resolved_path; 443#else 444 if ((resolved_path = malloc(MAXPATHLEN+1)) == NULL) 445 return NULL; 446 if (realpath(path, resolved_path) == NULL) { 447 free(resolved_path); 448 LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path); 449 return NULL; 450 } 451 /* Safe some memory */ 452 char *tmp; 453 if ((tmp = strdup(resolved_path)) == NULL) { 454 free(resolved_path); 455 return NULL; 456 } 457 free(resolved_path); 458 resolved_path = tmp; 459 return resolved_path; 460#endif 461} 462 463/** 464 * Returns pointer to static buffer with basename of path 465 **/ 466const char *basename_safe(const char *path) 467{ 468 static char buf[MAXPATHLEN+1]; 469 strlcpy(buf, path, MAXPATHLEN); 470 return basename(buf); 471} 472 473/** 474 * extended strtok allows the quoted strings 475 * modified strtok.c in glibc 2.0.6 476 **/ 477char *strtok_quote(char *s, const char *delim) 478{ 479 static char *olds = NULL; 480 char *token; 481 482 if (s == NULL) 483 s = olds; 484 485 /* Scan leading delimiters. */ 486 s += strspn (s, delim); 487 if (*s == '\0') 488 return NULL; 489 490 /* Find the end of the token. */ 491 token = s; 492 493 if (token[0] == '\"') { 494 token++; 495 s = strpbrk (token, "\""); 496 } else { 497 s = strpbrk (token, delim); 498 } 499 500 if (s == NULL) { 501 /* This token finishes the string. */ 502 olds = strchr (token, '\0'); 503 } else { 504 /* Terminate the token and make OLDS point past it. */ 505 *s = '\0'; 506 olds = s + 1; 507 } 508 return token; 509} 510 511int set_groups(AFPObj *obj, struct passwd *pwd) 512{ 513 if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) 514 LOG(log_error, logtype_afpd, "initgroups(%s, %d): %s", pwd->pw_name, pwd->pw_gid, strerror(errno)); 515 516 if ((obj->ngroups = getgroups(0, NULL)) < 0) { 517 LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno)); 518 return -1; 519 } 520 521 if (obj->groups) 522 free(obj->groups); 523 if (NULL == (obj->groups = calloc(obj->ngroups, sizeof(gid_t))) ) { 524 LOG(log_error, logtype_afpd, "login: %s calloc: %d", obj->ngroups); 525 return -1; 526 } 527 528 if ((obj->ngroups = getgroups(obj->ngroups, obj->groups)) < 0 ) { 529 LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno)); 530 return -1; 531 } 532 533 return 0; 534} 535 536#define GROUPSTR_BUFSIZE 1024 537const char *print_groups(int ngroups, gid_t *groups) 538{ 539 static char groupsstr[GROUPSTR_BUFSIZE]; 540 int i; 541 char *s = groupsstr; 542 543 if (ngroups == 0) 544 return "-"; 545 546 for (i = 0; (i < ngroups) && (s < &groupsstr[GROUPSTR_BUFSIZE]); i++) { 547 s += snprintf(s, &groupsstr[GROUPSTR_BUFSIZE] - s, " %u", groups[i]); 548 } 549 550 return groupsstr; 551} 552