RFC: obexapp - virtual root folder for each device
Maksim Yevmenkin
maksim.yevmenkin at gmail.com
Mon Apr 13 16:40:14 PDT 2009
dear freebsd-bluetooth@ users,
please find attached patch to obexapp port that add new feature of
virtualizing root folder for each client device. this work was
inspired by and based work done by 'mi' < mi -plus- thun -at- aldan
-dot- algebra -dot- com>
background
various bluetooth obex profiles do not have notion of user
credentials. that could be very inconvenient when multiple client
devices want to store data on the same obex sever. the example that
was given to me by 'mi' is when two people (say, wife and husband)
want to back up their complete phone books onto the same server. the
problem is that most devices use some well known name, such as
phonebook.vcf and there is no obvious way to override it.
how does it work
obexapp now has new options '-R' that would turn new feature on.
first, default root folder is set as it was before (see man page '-r'
option). when client is connected, client's bd_addr is resolved to a
human readable name via bt_gethostbyaddr(3) call. if bd_addr is
resolved then obexapp will check for the subdirectory, under current
root, with the resolved name. if name was not resolved or resolution
has failed, then obexapp will look for a subdirectory that matches
client's bd_addr, i.e. '01:02:03:04:05:06'. if that fails, then
obexapp will look for "default" subdirectory. if later fails as well,
connection is terminated. if virtual root is found, obexapp will
chroot(2) into it.
possible setup
- create 'obex' user and 'obex' group
- create '/var/spool/obex' (or whatever you want for default root)
owned by 'obex' user/group
- user 'foo' creates ~/private directory under his home directory with
0700 permissions
- admin setups 'obex' directory under foo's ~/private/ directory with
0770 permissions, this directory is owned by 'obex' user. group is set
to foo's group
- admin setups symlink in /var/spool/obex/ called 'foo_cell' that
points to ~foo/private/obex
- admin adds entry in the /etc/bluetooth/hosts file to assign
'foo_cell' foo's cell phone bd_addr
- admin run obexapp server as 'obexapp -s -r /var/spool/obex -R -u obex -C 1'
every time foo's uses cell phone to send data to the obex server, the
data will end up in foo's ~/private/obex directory.
please give it a try and let me know it works.
thanks,
max
-------------- next part --------------
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 13 Apr 2009 21:25:08 -0000
@@ -65,7 +65,7 @@
{
struct sigaction sa;
char *ep = NULL, *pri_name = NULL;
- int n, service, noninteractive;
+ int n, service, noninteractive, 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)
@@ -119,7 +119,7 @@
/* Process command line options */
service = noninteractive = 0;
- while ((n = getopt(argc, argv, "a:A:cC:dDfhl:m:nr:Ssu:")) != -1) {
+ 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 */
@@ -217,6 +217,11 @@
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)
usage(basename(argv[0]));
@@ -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 */
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 13 Apr 2009 23:15:03 -0000
@@ -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,43 @@
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 a subdirectory 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 a subdirectory that matches resolved name (if any);
+.It
+.Nm
+will check for a subdirectory that matches connecting device's BD_ADDR;
+.It
+.Nm
+will check for a subdirectory, named
+.Dq default ;
+.El
+If none of the above matches, then the connection to the client is terminated.
+Otherwise,
+.Nm
+will change default root folder the the found subdirectory.
+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 +368,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 13 Apr 2009 21:22:29 -0000
@@ -87,8 +87,8 @@
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 vroot : 1; /* virtualize device's root */
unsigned reserved : 2;
/* local SDP session (server only) */
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 13 Apr 2009 23:10:31 -0000
@@ -1,6 +1,8 @@
/*
* server.c
- *
+ */
+
+/*-
* Copyright (c) 2002 Maksim Yevmenkin <m_evmenkin at yahoo.com>
* All rights reserved.
*
@@ -89,6 +91,11 @@
static char const * const ls_parent_folder =
"<parent-folder/>\n";
+static int obexapp_server_set_root
+ (context_p context);
+static int obexapp_server_set_device_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 +121,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 +137,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 +148,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 +156,8 @@
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_root(context) < 0)
goto done;
- }
log_debug("%s(): Entering event processing loop...", __func__);
@@ -227,6 +181,155 @@
} /* obexapp_server */
/*
+ * Set server root
+ */
+
+int
+obexapp_server_set_root(context_p context)
+{
+ struct passwd *pw;
+ char *ep;
+ uid_t uid;
+
+ if (context->user != NULL) {
+ uid = strtoul(context->user, &ep, 10);
+ if (*ep != '\0')
+ pw = getpwnam(context->user);
+ else
+ pw = getpwuid(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);
+ } else
+ pw = NULL;
+
+ /* 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);
+ }
+
+ /* Set device specific root */
+ if (context->vroot && obexapp_server_set_device_root(context) <= 0)
+ return (-1);
+
+ log_debug("%s(): Using root %s", __func__, context->root);
+
+ 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);
+
+ log_debug("%s(): Secure mode enabled", __func__);
+ }
+
+ 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);
+ return (-1);
+ }
+
+ if (setuid(pw->pw_uid) < 0) {
+ log_err("%s(): Could not setuid(%d). %s (%d)",
+ __func__, pw->pw_uid, strerror(errno), errno);
+ return (-1);
+ }
+
+ log_debug("%s(): Running as uid=%d, gid=%d",
+ __func__, getuid(), getgid());
+ }
+
+ return (0);
+} /* obexapp_server_set_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 (stat(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);
+ }
+
+ if (!S_ISDIR(sb.st_mode)) {
+ log_debug("%s(): Ignoring %s/%s. Not a directory",
+ __func__, context->root, root[n]);
+ continue;
+ }
+
+ 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);
+ }
+
+ return (1);
+ }
+
+ log_notice("%s(): Could not set device specific root for the device " \
+ "bdaddr %s (%s)", __func__, root[1],
+ root[0]? root[0] : "-no-name-");
+
+ return (0);
+} /* obexapp_server_set_device_root */
+
+/*
* Process OBEX_EV_REQHINT event
*/
@@ -565,6 +668,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 10 Apr 2009 18:26:08 -0000
@@ -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 10 Apr 2009 18:05:23 -0000
@@ -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));
More information about the freebsd-bluetooth
mailing list