From owner-freebsd-bluetooth@FreeBSD.ORG Tue Apr 14 01:06:06 2009 Return-Path: Delivered-To: freebsd-bluetooth@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 4BF301065670 for ; Tue, 14 Apr 2009 01:06:06 +0000 (UTC) (envelope-from mi+thun@aldan.algebra.com) Received: from mail4.sea5.speakeasy.net (mail4.sea5.speakeasy.net [69.17.117.6]) by mx1.freebsd.org (Postfix) with ESMTP id 23C5F8FC13 for ; Tue, 14 Apr 2009 01:06:05 +0000 (UTC) (envelope-from mi+thun@aldan.algebra.com) Received: (qmail 3298 invoked from network); 14 Apr 2009 00:39:24 -0000 Received: from aldan.algebra.com (mi@[216.254.65.224]) (envelope-sender ) by mail4.sea5.speakeasy.net (qmail-ldap-1.03) with AES256-SHA encrypted SMTP for ; 14 Apr 2009 00:39:23 -0000 Message-ID: <49E3DB35.4030601@aldan.algebra.com> Date: Mon, 13 Apr 2009 20:39:17 -0400 From: "Mikhail T." User-Agent: Thunderbird 2.0.0.21 (X11/20090407) MIME-Version: 1.0 To: Maksim Yevmenkin References: In-Reply-To: Content-Type: multipart/mixed; boundary="------------000404000405070103000206" Cc: "freebsd-bluetooth@freebsd.org" Subject: Re: RFC: obexapp - virtual root folder for each device X-BeenThere: freebsd-bluetooth@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Using Bluetooth in FreeBSD environments List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 14 Apr 2009 01:06:06 -0000 This is a multi-part message in MIME format. --------------000404000405070103000206 Content-Type: text/plain; charset=KOI8-R Content-Transfer-Encoding: 8bit Maksim Yevmenkin ΞΑΠΙΣΑΧ(ΜΑ): > 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. > I think, Maksim's proposal is suboptimal, because the uploaded files end up owned by the new user obex, rather than the actual user (foo in the above example), which will make file-manipulations on the system itself more difficult (obex-owned will they not have 600 permissions?). It also necessitates creation of a new UID without the security benefit of having the daemon run under that UID permanently (but only switching after accepting each connection)... My proposal -- discussed with Maksim at length -- would *derive the user-ID from the ownership of the link*. The sample set up would then be as follows. Suppose, user named wallaby has a device with BD_ADDR 01:02:03:04:05:06. The user would create a subdirectory for their bluetooth files (or use something existing, like ~/Desktop/): wallaby@tasmania (11) mkdir ~/bluetooth root will then -- on wallaby's request -- tell obexapp about it: root@tasmania (111) ln -s ~wallaby/bluetooth /var/spool/obex/01:02:03:04:05:06 root@tasmania (112) chown -h wallaby ~wallaby/bluetooth /var/spool/obex/01:02:03:04:05:06 The second of the above root's lines is what will -- under my proposal -- tell obexapp-daemon, who shall own any files uploaded by the device. If started as root, instead of becoming 'obex', obexapp will become 'wallaby'... Upon accepting a connection, the server will do the same lookups as Maksim's version is doing, cd/chroot into the subdirectory, and setuid to the proper UID (such as wallaby's in this example). The attached patch can be dropped into /usr/ports/comms/obexapp/files/ . The only functionality still missing from it compared to Maksim's is the bt_gethostbyaddr(3) lookup -- only the numeric BD_ADDR is currently considered. This is not a significant difference between proposals, though... My proposal has other, not so significant differences -- it allows the BD_ADDR-entries in /var/spool/obex to be non-directories (files, broken links a'la /etc/make.conf, even sockets). Even if a chdir into such an entry fails, the ID of the entry's owner will still be used to determine, which user shall own any uploaded files, etc. even though all such files will end up in the same directory (as they do with the current version of obexapp). Maksim thought, offering such flexibility would be too confusing... I agree, that chroot-ing (rather than merely chdir-ing) into such a "virtual root" directory makes sense. The only material difference between our proposals is my deriving the desired UID from the ownership of the found BD_ADDR entry vs. Maksim's always using the user specified on command-line (such as 'obex'). Yours, -mi --------------000404000405070103000206 Content-Type: text/plain; name="patch-uid" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="patch-uid" --- obexapp.1 2007-05-21 11:55:35.000000000 -0400 +++ obexapp.1 2009-04-09 22:15:22.000000000 -0400 @@ -217,4 +217,30 @@ This only works if server was started as root. .El +.Sh Per-user configurations +When accepting connections in server mode, +.Nm +will check, if there is an entry named after the connecting device's +BD_ADDR in the root-path (default or specified by the +.Fl r +option). If found, the entry will be used as follows: +.Bl -enum -offset indent -compact +.It +The group ID will be changed to that, which owns the entry. +.It +If +.Nm +is running as root, it will change to the user, that owns the entry. +.It +If the entry is itself a directory and chdir into it succeeds, it will +be used as the top-level. +.El +This allows the same system to intelligently distinguish different Bluetooth 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 @@ -326,4 +352,11 @@ 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 --- transport.c 2007-05-21 11:55:35.000000000 -0400 +++ transport.c 2009-04-09 20:53:27.000000000 -0400 @@ -271,4 +272,7 @@ addr.rfcomm_channel, getpid()); + memcpy(&context->raddr, &addr.rfcomm_bdaddr, + sizeof(context->raddr)); + if (daemon(1, 0) < 0) { log_err("%s(): Could not daemon. %s (%d)", --- work/obexapp/server.c 2009-04-10 12:29:53.000000000 -0400 +++ work/obexapp/server.c 2009-04-13 20:33:16.000000000 -0400 @@ -110,5 +110,4 @@ * OBEX server */ - int obexapp_server(obex_t *handle) @@ -118,4 +117,8 @@ int error = -1; struct sockaddr_rfcomm addr; + struct stat sb; + const char *subdir = bt_ntoa(&context->raddr, NULL); + uid_t uid = 0; + gid_t gid = getgid(); context->ss = sdp_open_local(NULL); @@ -143,4 +146,6 @@ goto done; } + uid = pw->pw_uid; + gid = pw->pw_gid; } @@ -171,4 +176,36 @@ } + if (chdir(context->root) < 0) { + log_err("%s(): Could not chdir(%s). %s (%d)", + __func__, context->root, strerror(errno), errno); + goto done; + } + + log_debug("%s(): checking for %s/%s subdirectory", __func__, + context->root, subdir); + if (lstat(subdir, &sb) == 0) { + log_debug("%s(): %s/%s exists and belongs to uid %d", + __func__, context->root, subdir, sb.st_uid); + uid = sb.st_uid; + gid = sb.st_gid; + if (chdir(subdir)) + log_debug("%s(): chdir to %s failed: %s. Will remain " + "on top (uid %ld).", __func__, subdir, strerror(errno), + (long)uid); + else + getwd(context->root); + + log_debug("%s(): using %s", __func__, context->root); + } else switch (errno) { + case ENOENT: + log_debug("%s(): %s/%s not found", __func__, + context->root, subdir); + break; + default: + log_err("%s(): %s/%s: %s", __func__, context->root, subdir, + strerror(errno)); + goto done; + } + if (getuid() == 0) { if (context->secure) { @@ -183,25 +220,19 @@ } - 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) { + if (uid) { + if (setuid(uid) < 0) { log_err("%s(): Could not setuid(%d). %s (%d)", - __func__, pw->pw_uid, strerror(errno), + __func__, 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); - goto done; + if (gid != getgid() && setgid(gid) < 0) { + log_err("%s(): Could not setgid(%d). %s (%d)", + __func__, gid, strerror(errno), + errno); + goto done; + } } --------------000404000405070103000206--