dtms

changeset 4:db4bb4b5905a tip

security fixes: - refuse to run as root - drop privileges without an explicit -u argument when running setuid-root fixed argument parsing fixed mem leak added install and uninstall rules (Makefile)
author Eleni Maria Stea <eleni@mutantstargoat.com>
date Sun, 15 May 2016 00:15:45 +0300
parents bf6dda0e9daf
children
files Makefile src/main.c
diffstat 2 files changed, 79 insertions(+), 43 deletions(-) [+]
line diff
     1.1 --- a/Makefile	Fri May 13 23:02:26 2016 +0300
     1.2 +++ b/Makefile	Sun May 15 00:15:45 2016 +0300
     1.3 @@ -1,3 +1,6 @@
     1.4 +# change the following line to install to a different prefix
     1.5 +PREFIX = /usr/local
     1.6 +
     1.7  src = $(wildcard src/*.c)
     1.8  obj = $(src:.c=.o)
     1.9  dep = $(obj:.o=.d)
    1.10 @@ -6,10 +9,7 @@
    1.11  dbg = -g
    1.12  opt = -O0
    1.13  
    1.14 -CC = gcc
    1.15 -INCLUDE = -I/usr/include
    1.16 -CFLAGS = -pedantic -Wall -std=c99 -D_XOPEN_SOURCE=600 $(dbg) $(INCLUDE)
    1.17 -LDFLAGS = -L/usr/lib $(libs)
    1.18 +CFLAGS = -pedantic -Wall -std=gnu99 -DPREFIX=\"$(PREFIX)\" $(dbg)
    1.19  
    1.20  $(bin): $(obj)
    1.21  	$(CC) -o $@ $(obj) $(LDFLAGS)
    1.22 @@ -22,3 +22,19 @@
    1.23  .PHONY: clean
    1.24  clean:
    1.25  	rm -f $(obj) $(bin) $(dep)
    1.26 +
    1.27 +.PHONY: install
    1.28 +install: $(bin)
    1.29 +	mkdir -p $(PREFIX)/bin $(PREFIX)/share/dtms
    1.30 +	cp $(bin) $(PREFIX)/bin/$(bin)
    1.31 +	chown root $(PREFIX)/bin/$(bin)
    1.32 +	chmod +s $(PREFIX)/bin/$(bin)
    1.33 +	cp data/dtms.mp3 $(PREFIX)/share/dtms/dtms.mp3
    1.34 +
    1.35 +.PHONY: uninstall
    1.36 +uninstall:
    1.37 +	rm -f $(PREFIX)/bin/$(bin)
    1.38 +	rm -f $(PREFIX)/share/dtms/dtms.mp3
    1.39 +	rmdir $(PREFIX)/share/dtms
    1.40 +
    1.41 +
     2.1 --- a/src/main.c	Fri May 13 23:02:26 2016 +0300
     2.2 +++ b/src/main.c	Sun May 15 00:15:45 2016 +0300
     2.3 @@ -29,41 +29,58 @@
     2.4  
     2.5  static int parse_args(int argc, char **argv);
     2.6  static void print_help();
     2.7 -static void check_device(char *device_path);
     2.8 -static char* concatenate(char *mp_path, char *data_path);
     2.9 +static int check_device(char *device_path);
    2.10  
    2.11 -static int uid;
    2.12 -static char *path;
    2.13 +static int uid = -1;
    2.14 +static char *player_path;
    2.15  static char *dev_path;
    2.16  
    2.17 +#ifdef PREFIX
    2.18 +static const char* player_arg = PREFIX "/share/dtms/dtms.mp3";
    2.19 +#else
    2.20 +static const char* player_arg = "data/dtms.mp3";
    2.21 +#endif
    2.22 +
    2.23  int main(int argc, char **argv)
    2.24  {
    2.25      time_t last_touch_time = 0;
    2.26      int fd;
    2.27  
    2.28 -    if(parse_args(argc, argv))
    2.29 +    if(parse_args(argc, argv) == -1)
    2.30  	return 1;
    2.31  
    2.32 -    check_device(dev_path);
    2.33 +    if(getuid() == 0 && uid == -1) {
    2.34 +	fprintf(stderr, "It's a bad idea to run this program as root, either make it setuid-root, or use the -u option to specify an unprivileged user.\n");
    2.35 +	return 1;
    2.36 +    }
    2.37 +
    2.38 +    if(check_device(dev_path) == -1)
    2.39 +	return 1;
    2.40  
    2.41      if((fd = open(dev_path, O_RDONLY | O_NONBLOCK)) == -1) {
    2.42  	fprintf(stderr, "Failed to open device: %s, error: %s\n", dev_path, strerror(errno));
    2.43  	return 1;
    2.44      }
    2.45  
    2.46 +    if(uid == -1) {
    2.47 +	uid = getuid();
    2.48 +    }
    2.49 +
    2.50      if(seteuid(uid) == -1) {
    2.51  	perror("Set uid failed");
    2.52 -	uid = 0;
    2.53 +	return 1;
    2.54      }
    2.55  
    2.56      if(!uid) {
    2.57 -	const char *st = "/";
    2.58 -	if(strncmp(path, st, 1) != 0) {
    2.59 +	if(player_path[0] != '/') {
    2.60  	    fprintf(stderr, "If you run this program as root you should pass the absolute path to a valid mp3 player.\n");
    2.61  	    return 1;
    2.62  	}
    2.63      }
    2.64  
    2.65 +    char *cmd = malloc(strlen(player_path) + 1 + strlen(player_arg) + 1);
    2.66 +    sprintf(cmd, "%s %s", player_path, player_arg);
    2.67 +
    2.68      while(1) {
    2.69  	fd_set read_set;
    2.70  	FD_ZERO(&read_set);
    2.71 @@ -83,13 +100,16 @@
    2.72  	    time_t now = time(0);
    2.73  	    while(read(fd, buf, sizeof buf) > 0);
    2.74  	    if (now - last_touch_time > 2) {
    2.75 -		char *cmd = concatenate(path, "data/dtms.mp3");
    2.76  		system(cmd);
    2.77  		last_touch_time = now;
    2.78  	    }
    2.79  	}
    2.80      }
    2.81 +
    2.82 +    free(cmd);
    2.83      close(fd);
    2.84 +
    2.85 +    return 0;
    2.86  }
    2.87  
    2.88  static int parse_args(int argc, char **argv)
    2.89 @@ -99,39 +119,48 @@
    2.90  	    print_help();
    2.91  	    exit(0);
    2.92  	}
    2.93 -	if((strcmp(argv[i], "-p") == 0)) {
    2.94 -	    if(argv[i+1]) {
    2.95 -		path = argv[i+1];
    2.96 +
    2.97 +	else if((strcmp(argv[i], "-p") == 0)) {
    2.98 +	    if(argv[++i]) {
    2.99 +		player_path = argv[i];
   2.100  	    }
   2.101  	    else {
   2.102  		fprintf(stderr, "Invalid path. Please give the absolute path to an mp3 player.\n");
   2.103 -		exit(1);
   2.104 +		return -1;
   2.105  	    }
   2.106  	}
   2.107  
   2.108 -	if((strcmp(argv[i], "-u") == 0)) {
   2.109 -	    if(argv[i+1]) {
   2.110 -		struct passwd *passwd = getpwnam(argv[i+1]);
   2.111 +	else if((strcmp(argv[i], "-u") == 0)) {
   2.112 +	    if(argv[++i]) {
   2.113 +		struct passwd *passwd = getpwnam(argv[i]);
   2.114  		if(!passwd) {
   2.115 -		    fprintf(stderr, "Failed to get uid for: %s : %s.\n", argv[i+1], strerror(errno));
   2.116 -		    exit(1);
   2.117 +		    fprintf(stderr, "Failed to get uid for: %s : %s.\n", argv[i], strerror(errno));
   2.118 +		    return -1;
   2.119  		}
   2.120  		uid = passwd->pw_uid;
   2.121 +		if(uid == 0) {
   2.122 +		    fprintf(stderr, "You should pass an unprivileged username.\n");
   2.123 +		    return -1;
   2.124 +		}
   2.125  	    }
   2.126  	    else {
   2.127 -		fprintf(stderr, "Invalid username. Type -u `whoami`.\n");
   2.128 -		exit(1);
   2.129 +		fprintf(stderr, "Missing username.\n");
   2.130 +		return -1;
   2.131  	    }
   2.132  	}
   2.133 -	if((strcmp(argv[i], "-d") == 0)) {
   2.134 -	    if(argv[i+1]) {
   2.135 -		dev_path = argv[i+1];
   2.136 +	else if((strcmp(argv[i], "-d") == 0)) {
   2.137 +	    if(argv[++i]) {
   2.138 +		dev_path = argv[i];
   2.139  	    }
   2.140  	    else {
   2.141  		fprintf(stderr, "Invalid device file.\n");
   2.142 -		exit(1);
   2.143 +		return -1;
   2.144  	    }
   2.145  	}
   2.146 +	else {
   2.147 +	    fprintf(stderr, "Unknown argument: %s\n", argv[i]);
   2.148 +	    return -1;
   2.149 +	}
   2.150      }
   2.151      return 0;
   2.152  }
   2.153 @@ -149,25 +178,16 @@
   2.154      printf("./dtms -d /dev/usb/hiddev0 -u eleni -p /usr/bin/mpv\n");
   2.155  }
   2.156  
   2.157 -static void check_device(char *device_path)
   2.158 +static int check_device(char *device_path)
   2.159  {
   2.160      struct stat sb;
   2.161      if(stat(dev_path, &sb) == -1) {
   2.162  	perror("stat");
   2.163 -	exit(0);
   2.164 +	return -1;
   2.165      }
   2.166      if(((sb.st_mode & S_IFMT) != S_IFBLK) && ((sb.st_mode & S_IFMT) != S_IFCHR)) {
   2.167 -	fprintf(stderr, "Invalid device file.\n");
   2.168 -	exit(0);
   2.169 +	fprintf(stderr, "%s is not a device file.\n", device_path);
   2.170 +	return -1;
   2.171      }
   2.172 +    return 0;
   2.173  }
   2.174 -
   2.175 -static char *concatenate(char *mp_path, char *data_path)
   2.176 -{
   2.177 -    char *res = malloc(strlen(mp_path) + 1 + strlen(data_path) + 1);
   2.178 -    strcpy(res, mp_path);
   2.179 -    strcat(res, " ");
   2.180 -    strcat(res, data_path);
   2.181 -
   2.182 -    return res;
   2.183 -}