/** Create possibly many directories. * * @note that the input directory name is NOT treated as a constant. This is so that * if an error is returned, the 'directory' ptr points to the name of the file * which caused the error. * * @param dir path to directory to create. * @param mode for new directories. * @param uid to set on new directories, may be -1 to use effective uid. * @param gid to set on new directories, may be -1 to use effective gid. * @return * - 0 on success. * - -1 on failure. Error available as errno. */ int rad_mkdir(char *dir, mode_t mode, uid_t uid, gid_t gid) { int rcode, fd; char *p; /* * Try to make the dir. If it exists, chmod it. * If a path doesn't exist, that's OK. Otherwise * return with an error. * * Directories permissions are initially set so * that only we should have access. This prevents * an attacker removing them and swapping them * out for a link to somewhere else. * We change them to the correct permissions later. */ rcode = mkdir(dir, 0700); if (rcode < 0) { switch (errno) { case EEXIST: return 0; /* don't change permissions */ case ENOENT: break; default: return rcode; } /* * A component in the dir path doesn't * exist. Look for the LAST dir name. Try * to create that. If there's an error, we leave * the dir path as the one at which the * error occured. */ p = strrchr(dir, FR_DIR_SEP); if (!p || (p == dir)) return -1; *p = '\0'; rcode = rad_mkdir(dir, mode, uid, gid); if (rcode < 0) return rcode; /* * Reset the dir path, and try again to * make the dir. */ *p = FR_DIR_SEP; rcode = mkdir(dir, 0700); if (rcode < 0) return rcode; } /* else we successfully created the dir */ /* * Set the permissions on the directory we created * this should never fail unless there's a race. */ fd = open(dir, O_DIRECTORY); if (fd < 0) return -1; rcode = fchmod(fd, mode); if (rcode < 0) { close(fd); return rcode; } if ((uid != (uid_t)-1) || (gid != (gid_t)-1)) { rad_suid_up(); rcode = fchown(fd, uid, gid); rad_suid_down(); } close(fd); return rcode; }
/** Open a UDP listener for DHCPV4 * */ static int mod_open(fr_listen_t *li) { proto_dhcpv4_udp_t const *inst = talloc_get_type_abort_const(li->app_io_instance, proto_dhcpv4_udp_t); proto_dhcpv4_udp_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t); int sockfd, rcode; uint16_t port = inst->port; CONF_SECTION *server_cs; CONF_ITEM *ci; li->fd = sockfd = fr_socket_server_udp(&inst->ipaddr, &port, inst->port_name, true); if (sockfd < 0) { PERROR("Failed opening UDP socket"); error: return -1; } /* * Set SO_REUSEPORT before bind, so that all packets can * listen on the same destination IP address. */ if (1) { int on = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) { ERROR("Failed to set socket 'reuseport': %s", fr_syserror(errno)); close(sockfd); return -1; } } if (inst->broadcast) { int on = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { ERROR("Failed to set broadcast option: %s", fr_syserror(errno)); close(sockfd); return -1; } } /* * SUID up is really only needed if interface is set, OR port <1024. */ rad_suid_up(); rcode = fr_socket_bind(sockfd, &inst->ipaddr, &port, inst->interface); rad_suid_down(); if (rcode < 0) { close(sockfd); PERROR("Failed binding socket"); goto error; } thread->sockfd = sockfd; ci = cf_parent(inst->cs); /* listen { ... } */ rad_assert(ci != NULL); ci = cf_parent(ci); rad_assert(ci != NULL); server_cs = cf_item_to_section(ci); thread->name = fr_app_io_socket_name(thread, &proto_dhcpv4_udp, NULL, 0, &inst->ipaddr, inst->port, inst->interface); DEBUG("Listening on dhcpv4 address %s bound to virtual server %s", thread->name, cf_section_name2(server_cs)); return 0; }