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]"

Reply via email to