2009/4/14 Mikhail T. <[email protected]>: [...]
> Slow, painful, but still progress... :-) please find attached revised patch that implements mi's (aka Mikhail) initial suggestion, i.e. use lstat(2) instead of stat(2). i've also changed it a bit to allow both cases, i.e. (1) when virtual root folder is requested and -u <user> option is set, obexapp will always run as <user>; (2) when virtual root folder is requested and _no_ -u option was specified, obexapp will run as the owner of the found virtual root folder entry (where entry is either symlink or actual subdirectory under default root folder); i've also included a patch, submitted by Ronald Klop to disable spinner in client mode for non-interactive client sessions. [...] > To summarize, you seem willing to consider the owner of the matching > entry when determining, which UID to switch to when dropping > root-privileges after chroot. The only remaining disagreement is whether > to use lstat vs. stat for the purpose. It being, literally, a > one-character change in the code, you can go ahead and begin coding the > change to match your style preferences. > > In the mean time, consider the example I just gave, showing stat being a > security hole... yes, now i get it :) sorry for being such a bonehead :) hopefully the latest patch will work for everyone. thanks, max
Index: event.c =================================================================== RCS file: /usr/local/cvs/ports/obexapp/event.c,v retrieving revision 1.6 diff -u -r1.6 event.c --- event.c 5 Jan 2009 16:37:25 -0000 1.6 +++ event.c 14 Apr 2009 21:23:16 -0000 @@ -1,7 +1,7 @@ /* * event.c * - * Copyright (c) 2002 Maksim Yevmenkin <[email protected]> + * Copyright (c) 2002-2009 Maksim Yevmenkin <[email protected]> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +32,7 @@ #include <bluetooth.h> #include <obex.h> #include <stdio.h> +#include <unistd.h> #include "compat.h" #include "obexapp.h" @@ -131,7 +132,7 @@ log_debug("%s(): Made some progress...", __func__); - if (!context->server) { + if (!context->server && !context->ni && isatty(STDOUT_FILENO)) { static char spinner[] = "\\|/-"; static uint32_t spinner_idx = 0; Index: main.c =================================================================== RCS file: /usr/local/cvs/ports/obexapp/main.c,v retrieving revision 1.13 diff -u -r1.13 main.c --- main.c 23 Apr 2007 18:29:18 -0000 1.13 +++ main.c 14 Apr 2009 21:23:38 -0000 @@ -1,7 +1,7 @@ /* * main.c * - * Copyright (c) 2002 Maksim Yevmenkin <[email protected]> + * Copyright (c) 2002-2009 Maksim Yevmenkin <[email protected]> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -65,7 +65,7 @@ { struct sigaction sa; char *ep = NULL, *pri_name = NULL; - int n, service, noninteractive; + int n, service, detach; context_t context; obex_ctrans_t custfunc; @@ -83,7 +83,7 @@ /* Prepare context */ memset(&context, 0, sizeof(context)); context.tfd = context.sfd = -1; - context.detach = 1; + detach = 1; context.ls_size = OBEXAPP_BUFFER_SIZE; if ((context.ls = (char *) malloc(context.ls_size)) == NULL) @@ -118,8 +118,8 @@ custfunc.customdata = &context; /* Process command line options */ - service = noninteractive = 0; - while ((n = getopt(argc, argv, "a:A:cC:dDfhl:m:nr:Ssu:")) != -1) { + service = 0; + while ((n = getopt(argc, argv, "a:A:cC:dDfhl:m:nr:RsSu:")) != -1) { switch (n) { case 'a': if (!bt_aton(optarg, &context.raddr)) { @@ -180,7 +180,7 @@ break; case 'd': /* do not detach server */ - context.detach = 0; + detach = 0; break; case 'D': /* use stdin/stdout */ @@ -209,7 +209,7 @@ usage(basename(argv[0])); /* NOT REACHED */ - noninteractive = 1; + context.ni = 1; break; case 'r': /* root */ @@ -217,8 +217,13 @@ err(1, "Could not realpath(%s)", optarg); break; + case 'R': /* virtualize root for each device */ + context.vroot = 1; + context.secure = 1; + break; + case 's': /* server */ - if (noninteractive) + if (context.ni) usage(basename(argv[0])); /* NOT REACHED */ @@ -269,23 +274,10 @@ log_open("obexapp", pri_name, 0); /* Detach server (if required) */ - if (context.server && context.detach) { - pid_t pid = fork(); - - if (pid == (pid_t) -1) { - log_err("%s(): Could not fork. %s (%d)", - __func__, strerror(errno), errno); - exit(1); - } - - if (pid != 0) - exit(0); - - if (daemon(0, 0) < 0) { - log_err("%s(): Could not daemon. %s (%d)", - __func__, strerror(errno), errno); - exit(1); - } + if (context.server && detach && daemon(0, 0) < 0) { + log_err("%s(): Could not daemon. %s (%d)", + __func__, strerror(errno), errno); + exit(1); } /* Initialize OBEX */ @@ -305,7 +297,7 @@ if (context.server) n = obexapp_server(context.handle); - else if (noninteractive) + else if (context.ni) n = obexapp_non_interactive_client(context.handle, argc, argv); else n = obexapp_client(context.handle); Index: obexapp.1 =================================================================== RCS file: /usr/local/cvs/ports/obexapp/obexapp.1,v retrieving revision 1.15 diff -u -r1.15 obexapp.1 --- obexapp.1 21 May 2007 15:55:35 -0000 1.15 +++ obexapp.1 14 Apr 2009 21:24:08 -0000 @@ -1,6 +1,6 @@ .\" obexapp.1 .\" -.\" Copyright (c) 2001-2003 Maksim Yevmenkin <[email protected]> +.\" Copyright (c) 2001-2009 Maksim Yevmenkin <[email protected]> .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -27,7 +27,7 @@ .\" $Id: obexapp.1,v 1.15 2007/05/21 15:55:35 max Exp $ .\" $FreeBSD$ .\" -.Dd April 10, 2007 +.Dd April 14, 2009 .Dt OBEXAPP 1 .Os .Sh NAME @@ -54,7 +54,7 @@ .Ar parameters .Nm .Fl s -.Op Fl dDSh +.Op Fl dDSRh .Op Fl A Ar BD_ADDR .Fl C Ar channel .Op Fl m Ar MTU @@ -193,6 +193,12 @@ Defaults to the maximum supported value. .It Fl n Work in the non-interactive client mode. +.It Fl R +Virtualize root folder for each client device in server mode. +Will automatically turn on secure mode, i.e. +.Fl S +option. +Please read section below for a complete description. .It Fl r Ar path Specify root folder. Default root folder in the server mode is @@ -216,6 +222,57 @@ The value specified may be either a username or a numeric user id. This only works if server was started as root. .El +.Sh VIRTUAL ROOT FOLDERS +When accepting connections in server mode, +.Nm +will attempt to find an entry that would act as a virtual root +folder for the connecting device. +Virtual root folders must reside under default root folder which is set +with +.Fl r +option. +The rules are as follows: +.Bl -enum -offset indent -compact +.It +.Nm +will try to resolve connecting device's BD_ADDR using +.Xr bt_gethostbyaddr 3 +call and check for an entry that matches resolved name (if any); +.It +.Nm +will check for an entry that matches connecting device's BD_ADDR; +.It +.Nm +will check for an entry, named +.Dq default ; +.El +If none of the above matches, then the connection to the client is terminated. +Otherwise, +.Nm +will try to change default root folder the the found entry. +.Pp +If +.Fl u +option was specified, the +.Nm +will try to change to the specified user. +Otherwise +.Nm +will try change to the user, that owns the found entry. +That is, if the found entry is a symlink, the +.Nm +will try change to the user, that owns symlink and not to the user, that +owns the entry symlink points to. +.Pp +This allows the same system to intelligently distinguish different +client devices as belonging to different users. +An administrator can set up the subdirectories for +known devices under +.Pa /var/spool/obex +(or wherever, see +.Fl r +option) for each user, or even as symlinks to each user's home directory +(or a subdirectory thereof). .Sh LOCALE SUPPORT The .Nm @@ -325,6 +382,13 @@ .Dv ANY address and RFCOMM channel .Li 1 . +.It ln -s Ar /home/wallaby Ar /var/spool/obex/00:01:02:03:04:05 +.It chown -h wallaby Ar /var/spool/obex/00:01:02:03:04:05 +Whenever the device with BD_ADDR of 00:01:02:03:04:05 connects, +.Nm +running in server mode will switch to user ID +.Ar wallaby +and use their home directory as the top-level for the connection. .El .Ss Level 1 Information Access The first level involves the basic ability to put an object (such as a vCard) Index: obexapp.h =================================================================== RCS file: /usr/local/cvs/ports/obexapp/obexapp.h,v retrieving revision 1.9 diff -u -r1.9 obexapp.h --- obexapp.h 23 Apr 2007 18:29:18 -0000 1.9 +++ obexapp.h 14 Apr 2009 21:26:44 -0000 @@ -1,7 +1,7 @@ /* * obexapp.h * - * Copyright (c) 2002 Maksim Yevmenkin <[email protected]> + * Copyright (c) 2002-2009 Maksim Yevmenkin <[email protected]> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -87,9 +87,10 @@ unsigned server : 1; /* server mode? */ unsigned secure : 1; /* secure mode? */ unsigned done : 1; /* done? */ - unsigned detach : 1; /* detach server? */ unsigned fbs : 1; /* Folder Browsing Service */ - unsigned reserved : 2; + unsigned vroot : 1; /* virtualize device's root */ + unsigned ni : 1; /* non-interactive? */ + unsigned reserved : 1; /* local SDP session (server only) */ void *ss; @@ -111,6 +112,10 @@ uint8_t *sbuffer; int mtu; /* OBEX MTU */ + + /* credentials */ + uid_t uid; + gid_t gid; }; typedef struct context context_t; typedef struct context * context_p; Index: server.c =================================================================== RCS file: /usr/local/cvs/ports/obexapp/server.c,v retrieving revision 1.11 diff -u -r1.11 server.c --- server.c 9 Apr 2009 23:16:31 -0000 1.11 +++ server.c 14 Apr 2009 20:55:37 -0000 @@ -1,7 +1,7 @@ /* * server.c * - * Copyright (c) 2002 Maksim Yevmenkin <[email protected]> + * Copyright (c) 2002-2009 Maksim Yevmenkin <[email protected]> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -89,6 +89,10 @@ static char const * const ls_parent_folder = "<parent-folder/>\n"; +static int obexapp_server_set_initial_root (context_p context); +static int obexapp_server_set_device_root (context_p context); +static int obexapp_server_set_final_root (context_p context); + /* OBEX request handlers */ static obexapp_request_handler_t obexapp_server_request_connect; static obexapp_request_handler_t obexapp_server_request_disconnect; @@ -114,7 +118,6 @@ obexapp_server(obex_t *handle) { context_p context = (context_p) OBEX_GetUserData(handle); - struct passwd *pw = NULL; int error = -1; struct sockaddr_rfcomm addr; @@ -131,26 +134,6 @@ goto done; } - if (context->user != NULL) { - if (atoi(context->user) != 0) - pw = getpwuid(atoi(context->user)); - else - pw = getpwnam(context->user); - - if (pw == NULL) { - log_err("%s(): Unknown user %s", __func__, - context->user); - goto done; - } - } - - if (context->root[0] == '\0') { - if (pw == NULL) - strlcpy(context->root, OBEXAPP_ROOT_DIR, PATH_MAX); - else - strlcpy(context->root, pw->pw_dir, PATH_MAX); - } - log_info("%s: Starting OBEX server", __func__); if (OBEX_SetTransportMTU(handle, context->mtu, context->mtu) < 0) { @@ -162,7 +145,7 @@ addr.rfcomm_len = sizeof(addr); addr.rfcomm_family = AF_BLUETOOTH; addr.rfcomm_channel = context->channel; - memcpy(&addr.rfcomm_bdaddr, &context->raddr, sizeof(context->raddr)); + memcpy(&addr.rfcomm_bdaddr, &context->laddr, sizeof(context->laddr)); if (OBEX_ServerRegister(handle, (struct sockaddr *) &addr, sizeof(addr)) < 0) { @@ -170,40 +153,12 @@ goto done; } - if (getuid() == 0) { - if (context->secure) { - if (chroot(context->root) < 0) { - log_err("%s(): Could not chroot(%s). %s (%d)", - __func__, context->root, - strerror(errno), errno); - goto done; - } - - strlcpy(context->root, "/", PATH_MAX); - } - - if (pw != NULL) { - if (setgid(pw->pw_gid) < 0) { - log_err("%s(): Could not setgid(%d). %s (%d)", - __func__, pw->pw_gid, strerror(errno), - errno); - goto done; - } - - if (setuid(pw->pw_uid) < 0) { - log_err("%s(): Could not setuid(%d). %s (%d)", - __func__, pw->pw_uid, strerror(errno), - errno); - goto done; - } - } - } - - if (chdir(context->root) < 0) { - log_err("%s(): Could not chdir(%s). %s (%d)", - __func__, context->root, strerror(errno), errno); + if (obexapp_server_set_initial_root(context) < 0) + goto done; + if (context->vroot && obexapp_server_set_device_root(context) < 0) + goto done; + if (obexapp_server_set_final_root(context) < 0) goto done; - } log_debug("%s(): Entering event processing loop...", __func__); @@ -227,6 +182,165 @@ } /* obexapp_server */ /* + * Set initial server root + */ + +static int +obexapp_server_set_initial_root(context_p context) +{ + struct passwd *pw = NULL; + char *ep; + + if (context->user != NULL) { + context->uid = strtoul(context->user, &ep, 10); + if (*ep != '\0') + pw = getpwnam(context->user); + else + pw = getpwuid(context->uid); + + if (pw == NULL) { + log_err("%s(): Unknown user '%s'", + __func__, context->user); + return (-1); + } + + log_debug("%s(): Requested to run as '%s', uid=%d, gid=%d", + __func__, context->user, pw->pw_uid, pw->pw_gid); + + context->uid = pw->pw_uid; + context->gid = pw->pw_gid; + } else { + context->uid = getuid(); + context->gid = getgid(); + } + + /* Set default root */ + if (context->root[0] == '\0') { + if (pw == NULL) + strlcpy(context->root, OBEXAPP_ROOT_DIR, PATH_MAX); + else + strlcpy(context->root, pw->pw_dir, PATH_MAX); + } + + if (chdir(context->root) < 0) { + log_err("%s(): Could not chdir(%s). %s (%d)", + __func__, context->root, strerror(errno), errno); + return (-1); + } + + log_debug("%s(): Using initial root %s", __func__, context->root); + + return (0); +} /* obexapp_server_set_initial_root */ + +/* + * Set device specific server root + */ + +static int +obexapp_server_set_device_root(context_p context) +{ + char const *root[] = { NULL, NULL, NULL }; + struct hostent *he; + struct stat sb; + int n; + + n = 0; + + he = bt_gethostbyaddr((char const *) &context->raddr, + sizeof(bdaddr_t), AF_BLUETOOTH); + if (he != NULL) + root[n ++] = (char const *) he->h_name; + + root[n ++] = bt_ntoa(&context->raddr, NULL); + + root[n ++] = "default"; + + for (n = 0; n < 3; n ++) { + if (root[n] == NULL) + break; + + log_debug("%s(): Checking for %s/%s subdirectory", + __func__, context->root, root[n]); + + if (lstat(root[n], &sb) < 0) { + if (errno == ENOENT) + continue; + + log_err("%s(): Could not lstat(%s/%s). %s (%d)", + __func__, context->root, root[n], + strerror(errno), errno); + + return (-1); + } + + strlcat(context->root, "/", PATH_MAX); + strlcat(context->root, root[n], PATH_MAX); + + if (chdir(root[n]) < 0) { + log_err("%s(): Could not chdir(%s). %s (%d)", + __func__, context->root, strerror(errno), + errno); + return (-1); + } + + /* If user was not set before, take it from lstat() data */ + if (context->user == NULL) { + context->uid = sb.st_uid; + context->gid = sb.st_gid; + } + + log_debug("%s(): Using device specific root %s, uid=%d, gid=%d", + __func__, context->root, context->uid, context->gid); + + return (1); + } + + log_err("%s(): Could not find device specific root for the device " \ + "bdaddr %s (%s)", __func__, root[1], + root[0]? root[0] : "-no-name-"); + + return (-1); +} /* obexapp_server_set_device_root */ + +/* + * Finalize server root + */ + +static int +obexapp_server_set_final_root(context_p context) +{ + if (context->secure) { + if (chroot(context->root) < 0) { + log_err("%s(): Could not chroot(%s). %s (%d)", + __func__, context->root, + strerror(errno), errno); + return (-1); + } + + strlcpy(context->root, "/", PATH_MAX); + } + + if (context->gid != getgid() && setgid(context->gid) < 0) { + log_err("%s(): Could not setgid(%d). %s (%d)", + __func__, context->gid, strerror(errno), errno); + return (-1); + } + + if (context->uid != getuid() && setuid(context->uid) < 0) { + log_err("%s(): Could not setuid(%d). %s (%d)", + __func__, context->uid, strerror(errno), errno); + return (-1); + } + + log_notice("%s(): Using root %s; Secure mode %s; " + "Running as uid=%d, gid=%d", __func__, context->root, + context->secure? "enabled" : "disabled", getuid(), getgid()); + + return (0); +} /* obexapp_server_set_final_root */ + +/* * Process OBEX_EV_REQHINT event */ @@ -565,6 +679,15 @@ } } + if (chmod(context->temp, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) < 0) { + log_err("%s(): Could not chmod(%s). %s (%d)", + __func__, context->temp, + strerror(errno), errno); + + codes = obexapp_util_errno2response(errno); + goto done; + } + if (rename(context->temp, context->file) < 0) { log_err("%s(): Could not rename(%s, %s). %s (%d)", __func__, context->temp, Index: transport.c =================================================================== RCS file: /usr/local/cvs/ports/obexapp/transport.c,v retrieving revision 1.13 diff -u -r1.13 transport.c --- transport.c 21 May 2007 15:55:35 -0000 1.13 +++ transport.c 14 Apr 2009 21:26:02 -0000 @@ -1,7 +1,7 @@ /* * transport.c * - * Copyright (c) 2001 Maksim Yevmenkin <[email protected]> + * Copyright (c) 2001-2009 Maksim Yevmenkin <[email protected]> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -280,6 +280,9 @@ return (-1); } + memcpy(&context->raddr, &addr.rfcomm_bdaddr, + sizeof(context->raddr)); + return (1); } Index: util.c =================================================================== RCS file: /usr/local/cvs/ports/obexapp/util.c,v retrieving revision 1.14 diff -u -r1.14 util.c --- util.c 10 Apr 2009 17:26:03 -0000 1.14 +++ util.c 14 Apr 2009 21:26:08 -0000 @@ -1,7 +1,7 @@ /* * util.c * - * Copyright (c) 2002 Maksim Yevmenkin <[email protected]> + * Copyright (c) 2002-2009 Maksim Yevmenkin <[email protected]> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -425,9 +425,7 @@ * string, so always pass a copy. */ - strncpy(n, name, sizeof(n)); - n[sizeof(n) - 1] = '\0'; - + strlcpy(n, name, sizeof(n)); snprintf(temp, temp_size, "%s/XXXXXXXX", dirname(n)); return (mkstemp(temp));
_______________________________________________ [email protected] mailing list http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth To unsubscribe, send any mail to "[email protected]"
