Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Allow setuid open of any character device

Make the bpf device code generic so that the spawned setuid procket
executable is able to open any character device.

The code first checks the multiplex device (e.g., /dev/bpf). If it exists
and no error occurs, the fd is returned to the Erlang side.

If the muliplexing device does not exist or is not a character device,
the code will fall back to trying numbered devices.
  • Loading branch information...
commit bd91116d14d313e322508db3cada919631203a17 1 parent bde75a2
@msantos authored
View
7 c_src/procket.h
@@ -51,8 +51,11 @@
#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <ctype.h>
-#define PROCKET_VERSION "0.01"
+
+#define PROCKET_VERSION "0.03"
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)0)->sun_path)
#endif
@@ -93,6 +96,7 @@ typedef struct {
char *address; /* <port> or <ipaddr:port> */
char *ifname; /* network interface name */
int verbose; /* Debug messages */
+ char *dev; /* Open a character device */
in_addr_t ip; /* IP Address */
in_port_t port; /* Port */
@@ -102,7 +106,6 @@ typedef struct {
int type; /* socket type: SOCK_STREAM */
int protocol; /* socket protocol: IPPROTO_TCP */
int backlog; /* Listen backlog */
- int bpf; /* Open a bpf device */
} PROCKET_STATE;
View
80 c_src/procket_cmd.c
@@ -38,7 +38,8 @@
void procket_parse_address(PROCKET_STATE *ps);
int procket_pipe(PROCKET_STATE *ps);
int procket_open_socket(PROCKET_STATE *ps);
-int procket_open_bpf(PROCKET_STATE *ps);
+int procket_open_dev(PROCKET_STATE *ps);
+int procket_open_char_dev(char *dev);
void usage(PROCKET_STATE *ep);
@@ -58,7 +59,7 @@ main(int argc, char *argv[])
ps->type = SOCK_STREAM;
ps->protocol = IPPROTO_TCP;
- while ( (ch = getopt(argc, argv, "b:BF:hp:P:T:v:I:")) != -1) {
+ while ( (ch = getopt(argc, argv, "b:d:F:hp:P:T:v:I:")) != -1) {
switch (ch) {
case 'b': /* listen backlog */
ps->backlog = atoi(optarg);
@@ -80,8 +81,19 @@ main(int argc, char *argv[])
case 'I': /* Interface name */
IS_NULL(ps->ifname = strdup(optarg));
break;
- case 'B': /* Open a BPF device */
- ps->bpf = 1;
+ case 'd': { /* Open a character device */
+ char *p = NULL;
+
+ IS_NULL(ps->dev = strdup(optarg));
+
+ if (strlen(ps->dev) >= 32)
+ usage(ps);
+
+ for (p = ps->dev; *p; p++) {
+ if (!islower(*p) && !isdigit(*p))
+ usage(ps);
+ }
+ }
break;
case 'v':
ps->verbose++;
@@ -95,10 +107,10 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
- if (ps->bpf == 0) {
if (ps->path == NULL)
usage(ps);
+ if (ps->dev == NULL) {
if (ps->protocol == IPPROTO_TCP || ps->protocol == IPPROTO_UDP) {
if (argc == 0)
usage(ps);
@@ -112,7 +124,7 @@ main(int argc, char *argv[])
}
}
else {
- if (procket_open_bpf(ps) < 0) {
+ if (procket_open_dev(ps) < 0) {
(void)fprintf(stderr, "%s", strerror(errno));
exit (-errno);
}
@@ -208,17 +220,32 @@ procket_pipe(PROCKET_STATE *ps)
return (0);
}
-/* BPF support */
+/* character device support */
int
-procket_open_bpf(PROCKET_STATE *ps)
+procket_open_dev(PROCKET_STATE *ps)
{
char dev[MAXPATHLEN];
int i = 0;
+
+ (void)snprintf(dev, sizeof(dev), "/dev/%s", ps->dev);
+
+ ps->s = procket_open_char_dev(dev);
+
+ if (ps->s > -1)
+ return 0;
+
+ switch (errno) {
+ case ENOENT:
+ break;
+ default:
+ return -1;
+ }
+
for (i = 0; i < 255; i++) {
- (void)snprintf(dev, sizeof(dev), "/dev/bpf%d", i);
+ (void)snprintf(dev, sizeof(dev), "/dev/%s%d", ps->dev, i);
- ps->s = open(dev, O_RDWR);
+ ps->s = procket_open_char_dev(dev);
if (ps->s > -1)
return 0;
@@ -234,6 +261,37 @@ procket_open_bpf(PROCKET_STATE *ps)
return -1;
}
+ int
+procket_open_char_dev(char *dev)
+{
+ int fd = -1;
+ struct stat buf = {0};
+ int err = 0;
+
+ if ( (fd = open(dev, O_RDWR)) < 0)
+ return -1;
+
+ /* Test the file is a character device */
+ if (fstat(fd, &buf) < 0) {
+ err = errno;
+ goto ERR;
+ }
+
+ if (!S_ISCHR(buf.st_mode)) {
+ err = ENOENT;
+ goto ERR;
+ }
+
+ return fd;
+
+ERR:
+ if (fd > -1)
+ (void)close(fd);
+ errno = err;
+
+ return -1;
+}
+
void
usage(PROCKET_STATE *ps)
@@ -248,7 +306,7 @@ usage(PROCKET_STATE *ps)
#ifdef SO_BINDTODEVICE
" -I <name> interface [default: ANY]\n"
#endif
- " -B enable BPF support\n"
+ " -d <name> open device\n"
" -v verbose mode\n",
__progname
);
View
2  ebin/procket.app
@@ -1,7 +1,7 @@
{application, procket,
[
{description, "Low level socket operations"},
- {vsn, "0.02"},
+ {vsn, "0.03"},
{modules, [
procket,
packet,
View
30 src/procket.erl
@@ -223,19 +223,35 @@ get_switch({ip, Arg}) when is_tuple(Arg) -> inet_parse:ntoa(Arg);
get_switch({ip, Arg}) when is_list(Arg) -> Arg;
get_switch({interface, Name}) when is_list(Name) ->
- % An interface name is expected to consist of a reasonable
- % subset of all charactes, use a whitelist and extend it if needed
- SName = [C || C <- Name, ((C >= $a) and (C =< $z)) or ((C >= $A) and (C =< $Z))
- or ((C >= $0) and (C =< $9)) or (C == $.)],
- "-I " ++ SName;
+ case is_device(Name) of
+ true ->
+ "-I " ++ Name;
+ false ->
+ throw({bad_interface, Name})
+ end;
-get_switch({bpf, true}) ->
- "-B ";
+get_switch({dev, Dev}) when is_list(Dev) ->
+ case is_device(Dev) of
+ true ->
+ "-d " ++ Dev;
+ false ->
+ throw({bad_device, Dev})
+ end;
% Ignore any other arguments
get_switch(_Arg) ->
"".
+is_interface(Name) when is_list(Name) ->
+ % An interface name is expected to consist of a reasonable
+ % subset of all characters, use a whitelist and extend it if needed
+ Name == [C || C <- Name, (((C bor 32) >= $a) and ((C bor 32) =< $z))
+ or ((C >= $0) and (C =< $9)) or (C == $.)].
+
+is_device(Name) when is_list(Name) ->
+ Name == [C || C <- Name, (C >= $a) and (C =< $z))
+ or ((C >= $0) and (C =< $9))].
+
progname() ->
filename:join([
filename:dirname(code:which(?MODULE)),
Please sign in to comment.
Something went wrong with that request. Please try again.