static void lock_cachefile(int cachefile, int type) { struct flock fl = { .l_len = 0, .l_start = 0, .l_whence = SEEK_SET, }; fl.l_pid = getpid(); fl.l_type = type; if (verbose) output(2, "waiting on lock for cachefile\n"); if (fcntl(cachefile, F_SETLKW, &fl) == -1) { perror("fcntl F_SETLKW"); exit(1); } if (verbose) output(2, "took lock for cachefile\n"); } static void unlock_cachefile(int cachefile) { struct flock fl = { .l_len = 0, .l_start = 0, .l_whence = SEEK_SET, }; fl.l_pid = getpid(); fl.l_type = F_UNLCK; if (fcntl(cachefile, F_SETLK, &fl) == -1) { perror("fcntl F_UNLCK F_SETLK "); exit(1); } if (verbose) output(2, "dropped lock for cachefile\n"); } static void generate_sockets(void) { int fd, n; int cachefile; unsigned int nr_to_create = NR_SOCKET_FDS; unsigned int buffer[3]; cachefile = creat(cachefilename, S_IWUSR|S_IRUSR); if (cachefile < 0) { outputerr("Couldn't open cachefile for writing! (%s)\n", strerror(errno)); exit(EXIT_FAILURE); } lock_cachefile(cachefile, F_WRLCK); /* * Don't loop forever if all protos all are disabled. */ if (!do_specific_proto) { for (n = 0; n < (int)ARRAY_SIZE(no_protos); n++) { if (!no_protos[n]) break; } if (n >= (int)ARRAY_SIZE(no_protos)) nr_to_create = 0; } while (nr_to_create > 0) { struct socket_triplet st; if (shm->exit_reason != STILL_RUNNING) { close(cachefile); return; } for (st.family = 0; st.family < TRINITY_PF_MAX; st.family++) { if (do_specific_proto == TRUE) st.family = specific_proto; if (get_proto_name(st.family) == NULL) goto skip; BUG_ON(st.family >= ARRAY_SIZE(no_protos)); if (no_protos[st.family]) goto skip; if (sanitise_socket_triplet(&st) == -1) rand_proto_type(&st); fd = open_socket(st.family, st.type, st.protocol); if (fd > -1) { nr_to_create--; buffer[0] = st.family; buffer[1] = st.type; buffer[2] = st.protocol; n = write(cachefile, &buffer, sizeof(int) * 3); if (n == -1) { outputerr("something went wrong writing the cachefile!\n"); exit(EXIT_FAILURE); } if (nr_to_create == 0) goto done; } else { //outputerr("Couldn't open family:%d (%s)\n", st.family, get_proto_name(st.family)); } skip: /* check for ctrl-c */ if (shm->exit_reason != STILL_RUNNING) return; //FIXME: If we've passed -P and we're spinning here without making progress // then we should abort after a few hundred loops. } } done: unlock_cachefile(cachefile); output(1, "created %d sockets\n", nr_sockets); close(cachefile); } void close_sockets(void) { unsigned int i; int fd; int r = 0; struct linger ling = { .l_onoff = FALSE, .l_linger = 0 }; for (i = 0; i < nr_sockets; i++) { //FIXME: This is a workaround for a weird bug where we hang forevre // waiting for bluetooth sockets when we setsockopt. // Hopefully at some point we can remove this when someone figures out what's going on. if (shm->sockets[i].triplet.family == PF_BLUETOOTH) continue; /* Grab an fd, and nuke it before someone else uses it. */ fd = shm->sockets[i].fd; shm->sockets[i].fd = 0; /* disable linger */ r = setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger)); if (r) perror("setsockopt"); r = shutdown(fd, SHUT_RDWR); if (r) perror("shutdown"); if (close(fd) != 0) output(1, "failed to close socket [%d:%d:%d].(%s)\n", shm->sockets[i].triplet.family, shm->sockets[i].triplet.type, shm->sockets[i].triplet.protocol, strerror(errno)); } nr_sockets = 0; } void open_sockets(void) { int cachefile; unsigned int domain, type, protocol; unsigned int buffer[3]; int bytesread=-1; int fd; cachefile = open(cachefilename, O_RDONLY); if (cachefile < 0) { output(1, "Couldn't find socket cachefile. Regenerating.\n"); generate_sockets(); return; } lock_cachefile(cachefile, F_RDLCK); while (bytesread != 0) { bytesread = read(cachefile, buffer, sizeof(int) * 3); if (bytesread == 0) break; domain = buffer[0]; type = buffer[1]; protocol = buffer[2]; if ((do_specific_proto == TRUE && domain != specific_proto) || (domain < ARRAY_SIZE(no_protos) && no_protos[domain] == TRUE)) { output(1, "ignoring socket cachefile due to specific " "protocol request (or protocol disabled), " "and stale data in cachefile.\n"); regenerate: unlock_cachefile(cachefile); /* drop the reader lock. */ close(cachefile); unlink(cachefilename); generate_sockets(); return; } fd = open_socket(domain, type, protocol); if (fd < 0) { output(1, "Cachefile is stale. Need to regenerate.\n"); close_sockets(); goto regenerate; } /* check for ctrl-c */ if (shm->exit_reason != STILL_RUNNING) { close(cachefile); return; } } if (nr_sockets < NR_SOCKET_FDS) { output(1, "Insufficient sockets in cachefile (%d). Regenerating.\n", nr_sockets); goto regenerate; } output(1, "%d sockets created based on info from socket cachefile.\n", nr_sockets); unlock_cachefile(cachefile); close(cachefile); }
static void socket_destructor(struct object *obj) { struct socketinfo *si = &obj->sockinfo; struct linger ling = { .l_onoff = FALSE, .l_linger = 0 }; int fd; //FIXME: This is a workaround for a weird bug where we hang forevre // waiting for bluetooth sockets when we setsockopt. // Hopefully at some point we can remove this when someone figures out what's going on. if (si->triplet.family == PF_BLUETOOTH) return; /* Grab an fd, and nuke it before someone else uses it. */ fd = si->fd; si->fd = 0; /* disable linger */ (void) setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger)); (void) shutdown(fd, SHUT_RDWR); if (close(fd) != 0) output(1, "failed to close socket [%d:%d:%d].(%s)\n", si->triplet.family, si->triplet.type, si->triplet.protocol, strerror(errno)); } static void socket_dump(struct object *obj, bool global) { struct socketinfo *si = &obj->sockinfo; struct msg_objcreatedsocket objmsg; output(2, "socket fd:%d domain:%u (%s) type:0x%u protocol:%u\n", si->fd, si->triplet.family, get_domain_name(si->triplet.family), si->triplet.type, si->triplet.protocol); init_msgobjhdr(&objmsg.hdr, OBJ_CREATED_SOCKET, global, obj); objmsg.si.fd = si->fd; objmsg.si.triplet.family = si->triplet.family; objmsg.si.triplet.type = si->triplet.type; objmsg.si.triplet.protocol = si->triplet.protocol; sendudp((char *) &objmsg, sizeof(objmsg)); } static int open_sockets(void) { struct objhead *head; int bytesread = -1; int ret; head = get_objhead(OBJ_GLOBAL, OBJ_FD_SOCKET); head->destroy = &socket_destructor; head->dump = &socket_dump; cachefile = open(cachefilename, O_RDONLY); if (cachefile < 0) { output(1, "Couldn't find socket cachefile. Regenerating.\n"); ret = generate_sockets(); output(1, "created %d sockets\n", nr_sockets); return ret; } lock_cachefile(F_RDLCK); while (bytesread != 0) { unsigned int domain, type, protocol; unsigned int buffer[3]; int fd; bytesread = read(cachefile, buffer, sizeof(int) * 3); if (bytesread == 0) { if (nr_sockets == 0) goto regenerate; break; } domain = buffer[0]; type = buffer[1]; protocol = buffer[2]; if (domain >= TRINITY_PF_MAX) { output(1, "cachefile contained invalid domain %u\n", domain); goto regenerate; } if ((do_specific_domain == TRUE && domain != specific_domain) || (no_domains[domain] == TRUE)) { output(1, "ignoring socket cachefile due to specific " "protocol request (or protocol disabled), " "and stale data in cachefile.\n"); regenerate: unlock_cachefile(); /* drop the reader lock. */ close(cachefile); unlink(cachefilename); ret = generate_sockets(); return ret; } fd = open_socket(domain, type, protocol); if (fd < 0) { output(1, "Cachefile is stale. Need to regenerate.\n"); goto regenerate; } /* check for ctrl-c */ if (shm->exit_reason != STILL_RUNNING) { close(cachefile); return FALSE; } } output(1, "%d sockets created based on info from socket cachefile.\n", nr_sockets); unlock_cachefile(); close(cachefile); return TRUE; } struct socketinfo * get_rand_socketinfo(void) { struct object *obj; /* When using victim files, sockets can be 0. */ if (objects_empty(OBJ_FD_SOCKET) == TRUE) return NULL; obj = get_random_object(OBJ_FD_SOCKET, OBJ_GLOBAL); return &obj->sockinfo; } static int get_rand_socket_fd(void) { struct socketinfo *sockinfo; sockinfo = get_rand_socketinfo(); if (sockinfo == NULL) return -1; return sockinfo->fd; } int fd_from_socketinfo(struct socketinfo *si) { if (si != NULL) { if (!(ONE_IN(1000))) return si->fd; } return get_random_fd(); } static const struct fd_provider socket_fd_provider = { .name = "sockets", .enabled = TRUE, .open = &open_sockets, .get = &get_rand_socket_fd, }; REG_FD_PROV(socket_fd_provider);
static void lock_cachefile(int cachefile, int type) { struct flock fl = { .l_len = 0, .l_start = 0, .l_whence = SEEK_SET, }; fl.l_pid = getpid(); fl.l_type = type; if (verbose) output(2, "waiting on lock for cachefile\n"); if (fcntl(cachefile, F_SETLKW, &fl) == -1) { perror("fcntl F_SETLKW"); exit_main_fail(); } if (verbose) output(2, "took lock for cachefile\n"); } static void unlock_cachefile(int cachefile) { struct flock fl = { .l_len = 0, .l_start = 0, .l_whence = SEEK_SET, }; fl.l_pid = getpid(); fl.l_type = F_UNLCK; if (fcntl(cachefile, F_SETLK, &fl) == -1) { perror("fcntl F_UNLCK F_SETLK "); exit_main_fail(); } if (verbose) output(2, "dropped lock for cachefile\n"); } static unsigned int valid_proto(unsigned int family) { const char *famstr; famstr = get_proto_name(family); /* Not used for creating sockets. */ if (strncmp(famstr, "PF_UNSPEC", 9) == 0) return FALSE; if (strncmp(famstr, "PF_BRIDGE", 9) == 0) return FALSE; if (strncmp(famstr, "PF_SECURITY", 11) == 0) return FALSE; /* Not actually implemented (or now removed). */ if (strncmp(famstr, "PF_NETBEUI", 10) == 0) return FALSE; if (strncmp(famstr, "PF_ASH", 6) == 0) return FALSE; if (strncmp(famstr, "PF_ECONET", 9) == 0) return FALSE; if (strncmp(famstr, "PF_SNA", 6) == 0) return FALSE; if (strncmp(famstr, "PF_WANPIPE", 10) == 0) return FALSE; /* Needs root. */ if (orig_uid != 0) { if (strncmp(famstr, "PF_KEY", 6) == 0) return FALSE; if (strncmp(famstr, "PF_PACKET", 9) == 0) return FALSE; if (strncmp(famstr, "PF_LLC", 6) == 0) return FALSE; } return TRUE; } static int generate_sockets(void) { int fd, n, ret = FALSE; int cachefile; unsigned int nr_to_create = NR_SOCKET_FDS; unsigned int buffer[3]; cachefile = creat(cachefilename, S_IWUSR|S_IRUSR); if (cachefile == -1) outputerr("Couldn't open cachefile for writing! (%s)\n", strerror(errno)); else lock_cachefile(cachefile, F_WRLCK); /* * Don't loop forever if all protos all are disabled. */ if (!do_specific_proto) { for (n = 0; n < (int)ARRAY_SIZE(no_protos); n++) { if (!no_protos[n]) break; } if (n >= (int)ARRAY_SIZE(no_protos)) nr_to_create = 0; } while (nr_to_create > 0) { struct socket_triplet st; for (st.family = 0; st.family < TRINITY_PF_MAX; st.family++) { /* check for ctrl-c again. */ if (shm->exit_reason != STILL_RUNNING) goto out_unlock; if (do_specific_proto == TRUE) { st.family = specific_proto; //FIXME: If we've passed -P and we're spinning here without making progress // then we should abort after a few hundred loops. } if (get_proto_name(st.family) == NULL) continue; if (valid_proto(st.family) == FALSE) { if (do_specific_proto == TRUE) { outputerr("Can't do protocol %s\n", get_proto_name(st.family)); goto out_unlock; } else { continue; } } BUG_ON(st.family >= ARRAY_SIZE(no_protos)); if (no_protos[st.family]) continue; if (sanitise_socket_triplet(&st) == -1) rand_proto_type(&st); fd = open_socket(st.family, st.type, st.protocol); if (fd > -1) { nr_to_create--; if (cachefile != -1) { buffer[0] = st.family; buffer[1] = st.type; buffer[2] = st.protocol; n = write(cachefile, &buffer, sizeof(int) * 3); if (n == -1) { outputerr("something went wrong writing the cachefile!\n"); goto out_unlock; } } if (nr_to_create == 0) goto done; } else { //outputerr("Couldn't open family:%d (%s)\n", st.family, get_proto_name(st.family)); } } } done: ret = TRUE; output(1, "created %d sockets\n", nr_sockets); out_unlock: if (cachefile != -1) { unlock_cachefile(cachefile); close(cachefile); } return ret; } void close_sockets(void) { unsigned int i; int fd; struct linger ling = { .l_onoff = FALSE, .l_linger = 0 }; for (i = 0; i < nr_sockets; i++) { //FIXME: This is a workaround for a weird bug where we hang forevre // waiting for bluetooth sockets when we setsockopt. // Hopefully at some point we can remove this when someone figures out what's going on. if (shm->sockets[i].triplet.family == PF_BLUETOOTH) continue; /* Grab an fd, and nuke it before someone else uses it. */ fd = shm->sockets[i].fd; shm->sockets[i].fd = 0; /* disable linger */ (void) setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger)); (void) shutdown(fd, SHUT_RDWR); if (close(fd) != 0) output(1, "failed to close socket [%d:%d:%d].(%s)\n", shm->sockets[i].triplet.family, shm->sockets[i].triplet.type, shm->sockets[i].triplet.protocol, strerror(errno)); } nr_sockets = 0; } static int open_sockets(void) { int cachefile; unsigned int domain, type, protocol; unsigned int buffer[3]; int bytesread = -1; int fd; int ret; /* If we're doing victim files we probably don't care about sockets. */ //FIXME: Is this really true ? We might want to sendfile for eg if (victim_path != NULL) return TRUE; cachefile = open(cachefilename, O_RDONLY); if (cachefile < 0) { output(1, "Couldn't find socket cachefile. Regenerating.\n"); ret = generate_sockets(); return ret; } lock_cachefile(cachefile, F_RDLCK); while (bytesread != 0) { bytesread = read(cachefile, buffer, sizeof(int) * 3); if (bytesread == 0) break; domain = buffer[0]; type = buffer[1]; protocol = buffer[2]; if ((do_specific_proto == TRUE && domain != specific_proto) || (domain < ARRAY_SIZE(no_protos) && no_protos[domain] == TRUE)) { output(1, "ignoring socket cachefile due to specific " "protocol request (or protocol disabled), " "and stale data in cachefile.\n"); regenerate: unlock_cachefile(cachefile); /* drop the reader lock. */ close(cachefile); unlink(cachefilename); close_sockets(); ret = generate_sockets(); return ret; } fd = open_socket(domain, type, protocol); if (fd < 0) { output(1, "Cachefile is stale. Need to regenerate.\n"); goto regenerate; } /* check for ctrl-c */ if (shm->exit_reason != STILL_RUNNING) { close(cachefile); return FALSE; } } if (nr_sockets < NR_SOCKET_FDS) { output(1, "Insufficient sockets in cachefile (%d). Regenerating.\n", nr_sockets); goto regenerate; } output(1, "%d sockets created based on info from socket cachefile.\n", nr_sockets); unlock_cachefile(cachefile); close(cachefile); return TRUE; }
static void lock_cachefile(int type) { struct flock fl = { .l_len = 0, .l_start = 0, .l_whence = SEEK_SET, }; fl.l_pid = getpid(); fl.l_type = type; if (verbose) output(2, "waiting on lock for cachefile\n"); if (fcntl(cachefile, F_SETLKW, &fl) == -1) { perror("fcntl F_SETLKW"); return; } if (verbose) output(2, "took lock for cachefile\n"); } static void unlock_cachefile(void) { struct flock fl = { .l_len = 0, .l_start = 0, .l_whence = SEEK_SET, }; fl.l_pid = getpid(); fl.l_type = F_UNLCK; if (fcntl(cachefile, F_SETLK, &fl) == -1) { perror("fcntl F_UNLCK F_SETLK "); return; } if (verbose) output(2, "dropped lock for cachefile\n"); } static unsigned int valid_proto(unsigned int family) { const char *famstr; famstr = get_domain_name(family); /* Not used for creating sockets. */ if (strncmp(famstr, "UNSPEC", 9) == 0) return FALSE; if (strncmp(famstr, "BRIDGE", 9) == 0) return FALSE; if (strncmp(famstr, "SECURITY", 11) == 0) return FALSE; /* Not actually implemented (or now removed). */ if (strncmp(famstr, "NETBEUI", 10) == 0) return FALSE; if (strncmp(famstr, "ASH", 6) == 0) return FALSE; if (strncmp(famstr, "ECONET", 9) == 0) return FALSE; if (strncmp(famstr, "SNA", 6) == 0) return FALSE; if (strncmp(famstr, "WANPIPE", 10) == 0) return FALSE; /* Needs root. */ if (orig_uid != 0) { if (strncmp(famstr, "KEY", 6) == 0) return FALSE; if (strncmp(famstr, "PACKET", 9) == 0) return FALSE; if (strncmp(famstr, "LLC", 6) == 0) return FALSE; } return TRUE; } static bool write_socket_to_cache(struct socket_triplet *st) { unsigned int buffer[3]; int n; if (cachefile == -1) return FALSE; buffer[0] = st->family; buffer[1] = st->type; buffer[2] = st->protocol; n = write(cachefile, &buffer, sizeof(int) * 3); if (n == -1) { outputerr("something went wrong writing the cachefile! : %s\n", strerror(errno)); return FALSE; } return TRUE; } static bool generate_socket(unsigned int family, unsigned int protocol, unsigned int type) { struct socket_triplet st; int fd; st.family = family; st.type = type; st.protocol = protocol; fd = open_socket(st.family, st.type, st.protocol); if (fd > -1) { write_socket_to_cache(&st); return TRUE; } output(2, "Couldn't open socket %d:%d:%d. %s\n", family, type, protocol, strerror(errno)); return FALSE; } static bool generate_specific_socket(int family) { struct socket_triplet st; int fd; st.family = family; BUG_ON(st.family >= ARRAY_SIZE(no_domains)); if (no_domains[st.family]) return FALSE; if (get_domain_name(st.family) == NULL) return FALSE; if (valid_proto(st.family) == FALSE) { outputerr("Can't do protocol %s\n", get_domain_name(st.family)); return FALSE; } st.protocol = rnd() % 256; if (sanitise_socket_triplet(&st) == -1) rand_proto_type(&st); fd = open_socket(st.family, st.type, st.protocol); if (fd == -1) { output(0, "Couldn't open socket (%d:%d:%d). %s\n", st.family, st.type, st.protocol, strerror(errno)); return FALSE; } return write_socket_to_cache(&st); } #define NR_SOCKET_FDS 50 static bool generate_sockets(void) { int i, r, ret = FALSE; bool domains_disabled = FALSE; cachefile = creat(cachefilename, S_IWUSR|S_IRUSR); if (cachefile == -1) { outputerr("Couldn't open cachefile for writing! (%s)\n", strerror(errno)); return FALSE; } lock_cachefile(F_WRLCK); if (do_specific_domain == TRUE) { while (nr_sockets < NR_SOCKET_FDS) { ret = generate_specific_socket(specific_domain); if (ret == FALSE) return FALSE; } goto out_unlock; } /* * check if all domains are disabled. */ for (i = 0; i < (int)ARRAY_SIZE(no_domains); i++) { if (no_domains[i] == FALSE) { domains_disabled = FALSE; break; } else { domains_disabled = TRUE; } } if (domains_disabled == TRUE) { output(0, "All domains disabled!\n"); goto out_unlock; } for (i = 0; i < TRINITY_PF_MAX; i++) { const struct netproto *proto = net_protocols[i].proto; struct socket_triplet *triplets; unsigned int j; if (no_domains[i] == TRUE) continue; /* check for ctrl-c again. */ if (shm->exit_reason != STILL_RUNNING) goto out_unlock; if (proto == NULL) continue; if (proto->nr_triplets == 0) continue; triplets = proto->valid_triplets; for (j = 0; j < proto->nr_triplets; j++) ret |= generate_socket(triplets[j].family, triplets[j].protocol, triplets[j].type); if (proto->nr_privileged_triplets == 0) continue; if (orig_uid != 0) continue; triplets = proto->valid_privileged_triplets; for (j = 0; j < proto->nr_privileged_triplets; j++) ret |= generate_socket(triplets[j].family, triplets[j].protocol, triplets[j].type); } /* This is here temporarily until we have sufficient ->valid_proto's */ while (nr_sockets < NR_SOCKET_FDS) { r = rnd() % TRINITY_PF_MAX; for (i = 0; i < 10; i++) generate_specific_socket(r); } out_unlock: if (cachefile != -1) { unlock_cachefile(); close(cachefile); } return ret; }