dtms

annotate src/main.c @ 4:db4bb4b5905a

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
rev   line source
eleni@3 1 /*
eleni@3 2 * DTMS Copyright (C) 2016 Eleni Maria Stea <elene.mst@gmail.com>
eleni@3 3 *
eleni@3 4 * This program is free software: you can redistribute it and/or modify
eleni@3 5 * it under the terms of the GNU General Public License as published by
eleni@3 6 * the Free Software Foundation, either version 3 of the License, or
eleni@3 7 * (at your option) any later version.
eleni@3 8 *
eleni@3 9 * This program is distributed in the hope that it will be useful,
eleni@3 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
eleni@3 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
eleni@3 12 * GNU General Public License for more details.
eleni@3 13
eleni@3 14 * You should have received a copy of the GNU General Public License
eleni@3 15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
eleni@3 16 * */
eleni@3 17
eleni@0 18 #include <errno.h>
eleni@0 19 #include <fcntl.h>
eleni@0 20 #include <pwd.h>
eleni@0 21 #include <stdio.h>
eleni@0 22 #include <stdlib.h>
eleni@0 23 #include <string.h>
eleni@0 24 #include <sys/select.h>
eleni@0 25 #include <sys/stat.h>
eleni@0 26 #include <sys/types.h>
eleni@0 27 #include <time.h>
eleni@0 28 #include <unistd.h>
eleni@0 29
eleni@0 30 static int parse_args(int argc, char **argv);
eleni@0 31 static void print_help();
eleni@4 32 static int check_device(char *device_path);
eleni@0 33
eleni@4 34 static int uid = -1;
eleni@4 35 static char *player_path;
eleni@0 36 static char *dev_path;
eleni@0 37
eleni@4 38 #ifdef PREFIX
eleni@4 39 static const char* player_arg = PREFIX "/share/dtms/dtms.mp3";
eleni@4 40 #else
eleni@4 41 static const char* player_arg = "data/dtms.mp3";
eleni@4 42 #endif
eleni@4 43
eleni@0 44 int main(int argc, char **argv)
eleni@0 45 {
eleni@0 46 time_t last_touch_time = 0;
eleni@0 47 int fd;
eleni@0 48
eleni@4 49 if(parse_args(argc, argv) == -1)
eleni@0 50 return 1;
eleni@0 51
eleni@4 52 if(getuid() == 0 && uid == -1) {
eleni@4 53 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");
eleni@4 54 return 1;
eleni@4 55 }
eleni@4 56
eleni@4 57 if(check_device(dev_path) == -1)
eleni@4 58 return 1;
eleni@0 59
eleni@0 60 if((fd = open(dev_path, O_RDONLY | O_NONBLOCK)) == -1) {
eleni@0 61 fprintf(stderr, "Failed to open device: %s, error: %s\n", dev_path, strerror(errno));
eleni@0 62 return 1;
eleni@0 63 }
eleni@0 64
eleni@4 65 if(uid == -1) {
eleni@4 66 uid = getuid();
eleni@4 67 }
eleni@4 68
eleni@0 69 if(seteuid(uid) == -1) {
eleni@0 70 perror("Set uid failed");
eleni@4 71 return 1;
eleni@0 72 }
eleni@0 73
eleni@0 74 if(!uid) {
eleni@4 75 if(player_path[0] != '/') {
eleni@0 76 fprintf(stderr, "If you run this program as root you should pass the absolute path to a valid mp3 player.\n");
eleni@0 77 return 1;
eleni@0 78 }
eleni@0 79 }
eleni@0 80
eleni@4 81 char *cmd = malloc(strlen(player_path) + 1 + strlen(player_arg) + 1);
eleni@4 82 sprintf(cmd, "%s %s", player_path, player_arg);
eleni@4 83
eleni@0 84 while(1) {
eleni@0 85 fd_set read_set;
eleni@0 86 FD_ZERO(&read_set);
eleni@0 87 FD_SET(fd, &read_set);
eleni@0 88
eleni@0 89 int res;
eleni@0 90 while((res = select(fd + 1, &read_set, 0, 0, 0)) == -1 && errno == EINTR);
eleni@0 91 if(res < 0) {
eleni@0 92 perror("Select failed");
eleni@0 93 break;
eleni@0 94 }
eleni@0 95 if(res == 0) //nothing to read
eleni@0 96 continue;
eleni@0 97
eleni@0 98 if(FD_ISSET(fd, &read_set)) {
eleni@0 99 char buf[1024];
eleni@0 100 time_t now = time(0);
eleni@0 101 while(read(fd, buf, sizeof buf) > 0);
eleni@0 102 if (now - last_touch_time > 2) {
eleni@0 103 system(cmd);
eleni@0 104 last_touch_time = now;
eleni@0 105 }
eleni@0 106 }
eleni@0 107 }
eleni@4 108
eleni@4 109 free(cmd);
eleni@0 110 close(fd);
eleni@4 111
eleni@4 112 return 0;
eleni@0 113 }
eleni@0 114
eleni@0 115 static int parse_args(int argc, char **argv)
eleni@0 116 {
eleni@0 117 for(int i=1; i<argc; i++) {
eleni@0 118 if((strcmp(argv[i], "-h") == 0)) {
eleni@0 119 print_help();
eleni@0 120 exit(0);
eleni@0 121 }
eleni@4 122
eleni@4 123 else if((strcmp(argv[i], "-p") == 0)) {
eleni@4 124 if(argv[++i]) {
eleni@4 125 player_path = argv[i];
eleni@0 126 }
eleni@0 127 else {
eleni@0 128 fprintf(stderr, "Invalid path. Please give the absolute path to an mp3 player.\n");
eleni@4 129 return -1;
eleni@0 130 }
eleni@0 131 }
eleni@0 132
eleni@4 133 else if((strcmp(argv[i], "-u") == 0)) {
eleni@4 134 if(argv[++i]) {
eleni@4 135 struct passwd *passwd = getpwnam(argv[i]);
eleni@0 136 if(!passwd) {
eleni@4 137 fprintf(stderr, "Failed to get uid for: %s : %s.\n", argv[i], strerror(errno));
eleni@4 138 return -1;
eleni@0 139 }
eleni@0 140 uid = passwd->pw_uid;
eleni@4 141 if(uid == 0) {
eleni@4 142 fprintf(stderr, "You should pass an unprivileged username.\n");
eleni@4 143 return -1;
eleni@4 144 }
eleni@0 145 }
eleni@0 146 else {
eleni@4 147 fprintf(stderr, "Missing username.\n");
eleni@4 148 return -1;
eleni@0 149 }
eleni@0 150 }
eleni@4 151 else if((strcmp(argv[i], "-d") == 0)) {
eleni@4 152 if(argv[++i]) {
eleni@4 153 dev_path = argv[i];
eleni@0 154 }
eleni@0 155 else {
eleni@0 156 fprintf(stderr, "Invalid device file.\n");
eleni@4 157 return -1;
eleni@0 158 }
eleni@0 159 }
eleni@4 160 else {
eleni@4 161 fprintf(stderr, "Unknown argument: %s\n", argv[i]);
eleni@4 162 return -1;
eleni@4 163 }
eleni@0 164 }
eleni@0 165 return 0;
eleni@0 166 }
eleni@0 167
eleni@0 168 static void print_help()
eleni@0 169 {
eleni@0 170 printf("Options:\n");
eleni@0 171 printf("-h, prints this help\n");
eleni@0 172 printf("-d, path to the device\n");
eleni@0 173 printf("-p, path to the mp3 player\n");
eleni@0 174 printf("-u. username of the user that runs the program\n");
eleni@0 175 printf("--------\n");
eleni@0 176 printf("Examples:\n");
eleni@0 177 printf("--------\n");
eleni@0 178 printf("./dtms -d /dev/usb/hiddev0 -u eleni -p /usr/bin/mpv\n");
eleni@0 179 }
eleni@0 180
eleni@4 181 static int check_device(char *device_path)
eleni@0 182 {
eleni@0 183 struct stat sb;
eleni@0 184 if(stat(dev_path, &sb) == -1) {
eleni@0 185 perror("stat");
eleni@4 186 return -1;
eleni@0 187 }
eleni@0 188 if(((sb.st_mode & S_IFMT) != S_IFBLK) && ((sb.st_mode & S_IFMT) != S_IFCHR)) {
eleni@4 189 fprintf(stderr, "%s is not a device file.\n", device_path);
eleni@4 190 return -1;
eleni@0 191 }
eleni@4 192 return 0;
eleni@0 193 }