gods

a simple blocklist for ssh
Log | Files | Refs | README | LICENSE

main.c (5367B)


      1 #include <sys/stat.h>
      2 #include <sys/types.h>
      3 
      4 #include <err.h>
      5 #include <errno.h>
      6 #include <fcntl.h>
      7 #include <limits.h>
      8 #include <signal.h>
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 #include <unistd.h>
     13 
     14 #include "arg.h"
     15 
     16 #include "attack.h"
     17 #include "config.h"
     18 #include "fw.h"
     19 #include "ip.h"
     20 #include "parser.h"
     21 #include "queue.h"
     22 
     23 #define BUFSZ 512
     24 
     25 int fd_black;
     26 int fd_white;
     27 int ignore;
     28 int shutdown;
     29 char *argv0;
     30 
     31 static char buf[BUFSZ];
     32 SLIST_HEAD(lhead, attacker) head = SLIST_HEAD_INITIALIZER(head);
     33 
     34 void
     35 ban(struct attacker *a)
     36 {
     37 	if (a->list != GREY)
     38 		return;
     39 
     40 	a->nban++;
     41 	if (a->nban >= 0
     42 	&& fw_block(a->ip)
     43 	&& a->nban > max_try)
     44 		blacklist(a);
     45 }
     46 
     47 void
     48 unban(struct attacker *a, struct lhead *headp)
     49 {
     50 	struct attacker *ap;
     51 
     52 	if (a) {
     53 		if (fw_unblock(a->ip))
     54 			a->last = LLONG_MAX;
     55 		return;
     56 	}
     57 
     58 	SLIST_FOREACH(ap, headp, attackers)
     59 		if (ap->list == GREY && isexpire(ap))
     60 			if (fw_unblock(ap->ip))
     61 				ap->last = LLONG_MAX;
     62 }
     63 
     64 void
     65 init(struct attacker *a)
     66 {
     67 	strcpy(a->ip, ip);
     68 	a->nban = -immune_try;
     69 	a->last = attack;
     70 	a->list = GREY;
     71 }
     72 
     73 /*
     74  * XXX readline relies upon the assumption
     75  * that a line can be atmost BUFSZ long
     76  */
     77 char *
     78 readline(int fd)
     79 {
     80 	static char *next;
     81 	static int rem;
     82 	int n;
     83 
     84 	if (rem)
     85 		memmove(buf, next, rem);
     86 
     87 	if ((n = read(fd, buf+rem, BUFSZ-rem)) == -1)
     88 		err(1, "read failed");
     89 
     90 	rem += n;
     91 	for (int i = 0; i < rem; ++i)
     92 		if (buf[i] == '\n') {
     93 			buf[i] = '\0';
     94 			rem -= i+1;
     95 			next = (rem == 0) ? NULL : &buf[i+1];
     96 			return buf;
     97 		}
     98 
     99 	return NULL;
    100 }
    101 
    102 int
    103 foverlap(int fd1, int fd2)
    104 {
    105 	FILE *fp1, *fp2;
    106 	int r1, r2;
    107 	char buf1[16], buf2[16];
    108 
    109 	fp1 = fdopen(fd1, "r");
    110 	if (!fp1)
    111 		err(1, "fdopen failed");
    112 
    113 	fp2 = fdopen(fd2, "r");
    114 	if (!fp2)
    115 		err(1, "fdopen failed");
    116 
    117 	do {
    118 		r1 = fscanf(fp1, "%s\n", buf1);
    119 		if (r1 != EOF) {
    120 			do {
    121 				r2 = fscanf(fp2, "%s\n", buf2);
    122 				if (r2 != EOF
    123 				&&  !strcmp(buf1, buf2))
    124 						return 1;
    125 			} while (r2 != EOF);
    126 		}
    127 	} while(r1 != EOF);
    128 	return 0;
    129 }
    130 
    131 void
    132 cleanup()
    133 {
    134 	struct attacker *prev, *a;
    135 
    136 	prev= NULL;
    137 	SLIST_FOREACH(a, &head, attackers) {
    138 		if (prev)
    139 			free(prev);
    140 		prev = a;
    141 	}
    142 	free(prev);
    143 }
    144 
    145 void
    146 usage(void)
    147 {
    148 	fprintf(stderr, "usage: sdog [-d]\n");
    149 	exit(1);
    150 }
    151 
    152 void
    153 daemon_init(void)
    154 {
    155 	pid_t p;
    156 
    157 	if ((p = fork()) == -1)
    158 		err(1, "fork failed");
    159 	else if (p != 0)
    160 		exit(0);
    161 	if (setsid() == -1)
    162 		err(1, "setsid failed");
    163 	if (chdir("/etc/gods") == -1)
    164 		err(1, "chdir failed: %s", "/etc/gods");
    165 	umask(0);
    166 }
    167 
    168 void
    169 terminate(int a)
    170 {
    171 	shutdown = 1;
    172 }
    173 
    174 int
    175 main(int argc, char **argv)
    176 {
    177 	FILE *fp;
    178 	char *line;
    179 	char bf[16];
    180 	int debug, fd, found, ret;
    181 	struct attacker *a;
    182 	time_t now;
    183 
    184 	shutdown = debug = 0;
    185 
    186 	ARGBEGIN {
    187 		case 'd':
    188 			debug = 1;
    189 			break;
    190 		default:
    191 			usage();
    192 	} ARGEND
    193 
    194 	if (!debug)
    195 		daemon_init();
    196 
    197 	signal(SIGHUP, terminate);
    198 
    199 	fd = open(sshlog, O_RDONLY);
    200 	if (fd == -1)
    201 		err(1, "open failed: %s", sshlog);
    202 
    203 	fd_black = open(black_list, O_RDWR | O_CREAT | O_APPEND,
    204 			S_IRUSR | S_IWUSR);
    205 	if (fd_black == -1)
    206 		err(1, "open failed: %s", black_list);
    207 
    208 	fd_white = open(white_list, O_RDWR | O_CREAT | O_APPEND,
    209 			S_IRUSR | S_IWUSR);
    210 	if (fd_white == -1)
    211 		err(1, "open failed: %s", white_list);
    212 
    213 	if (foverlap(fd_black, fd_white))
    214 		errx(1, "blacklist and whitelist are not mutually exclusive.");
    215 
    216 	fp = fopen(black_list, "r");
    217 	if (!fp)
    218 		err(1, "fopen failed: %s", black_list);
    219 	do {
    220 		ret = fscanf(fp, "%s\n", bf);
    221 		if (ret == EOF)
    222 			break;
    223 		if (!isip(bf))
    224 			errx(1, "malformed ip in blacklist");
    225 		SLIST_FOREACH(a, &head, attackers)
    226 			if (!strcmp(a->ip, bf))
    227 				errx(1, "duplicate entry found in %s",
    228 				     black_list);
    229 		a = malloc(sizeof(struct attacker));
    230 		if (!a)
    231 			err(1, "malloc failed");
    232 		/*
    233 		 * Attackers generated from blacklist
    234 		 * will have nban and last set to 0
    235 		 */
    236 		strcpy(a->ip, bf);
    237 		a->list = BLACK;
    238 		fw_block(a->ip);
    239 		SLIST_INSERT_HEAD(&head, a, attackers);
    240 	} while (ret != EOF);
    241 	fclose(fp);
    242 
    243 	fp = fopen(white_list, "r");
    244 	if (!fp)
    245 		err(1, "fopen failed: %s", white_list);
    246 	do {
    247 		ret = fscanf(fp, "%s\n", bf);
    248 		if (ret == EOF)
    249 			break;
    250 		if (!isip(bf))
    251 			errx(1, "malformed ip in whitelist");
    252 		SLIST_FOREACH(a, &head, attackers)
    253 			if (!strcmp(a->ip, bf))
    254 				errx(1, "duplicate entry found in %s",
    255 				     white_list);
    256 		a = malloc(sizeof(struct attacker));
    257 		if (!a)
    258 			err(1, "malloc failed");
    259 		/*
    260 		 * Attackers generated from whitelist
    261 		 * will have nban and last set to 0
    262 		 */
    263 		strcpy(a->ip, bf);
    264 		a->list = WHITE;
    265 		SLIST_INSERT_HEAD(&head, a, attackers);
    266 	} while (ret != EOF);
    267 	fclose(fp);
    268 
    269 	now = time(NULL);
    270 	for ( ; ; ) {
    271 		if (shutdown)
    272 			break;
    273 
    274 		unban(NULL, &head);
    275 		if ((line = readline(fd)) == NULL) {
    276 			usleep(5);
    277 			continue;
    278 		}
    279 
    280 		if (parse(line, now) == -1) {
    281 			fprintf(stderr, "parse failed\n");
    282 			continue;
    283 		}
    284 		if (ignore)
    285 			continue;
    286 
    287 		a = NULL;
    288 		found = 0;
    289 		if (isattack(statmsg, preauth)) {
    290 			SLIST_FOREACH(a, &head, attackers)
    291 				if (!strcmp(a->ip, ip)) {
    292 					++found;
    293 					break;
    294 				}
    295 
    296 			if (!found) {
    297 				a = malloc(sizeof(struct attacker));
    298 				if (!a)
    299 					err(1, "malloc failed");
    300 				init(a);
    301 				SLIST_INSERT_HEAD(&head, a, attackers);
    302 			}
    303 			ban(a);
    304 		}
    305 
    306 		if (islogin(statmsg))
    307 			SLIST_FOREACH(a, &head, attackers)
    308 				if (!strcmp(a->ip, ip)) {
    309 					printf("attacker deleted\n");
    310 					SLIST_REMOVE(&head, a, attacker, attackers);
    311 					free(a);
    312 				}
    313 	}
    314 	cleanup();
    315 	return 0;
    316 }