/* * Do chroot, if requested. * * Switch UID and GID to what is specified in the config file */ static int switch_users(CONF_SECTION *cs) { bool do_suid = false; bool do_sgid = false; /* * Get the current maximum for core files. Do this * before anything else so as to ensure it's properly * initialized. */ if (fr_set_dumpable_init() < 0) { fr_perror("%s", main_config.name); return 0; } /* * Don't do chroot/setuid/setgid if we're in debugging * as non-root. */ if (rad_debug_lvl && (getuid() != 0)) return 1; if (cf_section_parse(cs, NULL, bootstrap_config) < 0) { fprintf(stderr, "%s: Error: Failed to parse user/group information.\n", main_config.name); return 0; } #ifdef HAVE_GRP_H /* * Get the correct GID for the server. */ server_gid = getgid(); if (gid_name) { struct group *gr; gr = getgrnam(gid_name); if (!gr) { fprintf(stderr, "%s: Cannot get ID for group %s: %s\n", main_config.name, gid_name, fr_syserror(errno)); return 0; } if (server_gid != gr->gr_gid) { server_gid = gr->gr_gid; do_sgid = true; } } #endif /* * Get the correct UID for the server. */ server_uid = getuid(); if (uid_name) { struct passwd *user; if (rad_getpwnam(cs, &user, uid_name) < 0) { fprintf(stderr, "%s: Cannot get passwd entry for user %s: %s\n", main_config.name, uid_name, fr_strerror()); return 0; } /* * We're not the correct user. Go set that. */ if (server_uid != user->pw_uid) { server_uid = user->pw_uid; do_suid = true; #ifdef HAVE_INITGROUPS if (initgroups(uid_name, server_gid) < 0) { fprintf(stderr, "%s: Cannot initialize supplementary group list for user %s: %s\n", main_config.name, uid_name, fr_syserror(errno)); talloc_free(user); return 0; } #endif } talloc_free(user); } /* * Do chroot BEFORE changing UIDs. */ if (chroot_dir) { if (chroot(chroot_dir) < 0) { fprintf(stderr, "%s: Failed to perform chroot %s: %s", main_config.name, chroot_dir, fr_syserror(errno)); return 0; } /* * Note that we leave chdir alone. It may be * OUTSIDE of the root. This allows us to read * the configuration from "-d ./etc/raddb", with * the chroot as "./chroot/" for example. After * the server has been loaded, it does a "cd * ${logdir}" below, so that core files (if any) * go to a logging directory. * * This also allows the configuration of the * server to be outside of the chroot. If the * server is statically linked, then the only * things needed inside of the chroot are the * logging directories. */ } #ifdef HAVE_GRP_H /* * Set the GID. Don't bother checking it. */ if (do_sgid) { if (setgid(server_gid) < 0){ fprintf(stderr, "%s: Failed setting group to %s: %s", main_config.name, gid_name, fr_syserror(errno)); return 0; } } #endif /* * The directories for PID files and logs must exist. We * need to create them if we're told to write files to * those directories. * * Because this creation is new in 3.0.9, it's a soft * fail. * */ if (main_config.write_pid) { char *my_dir; my_dir = talloc_strdup(NULL, run_dir); if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) { DEBUG("Failed to create run_dir %s: %s", my_dir, strerror(errno)); } talloc_free(my_dir); } if (default_log.dst == L_DST_FILES) { char *my_dir; my_dir = talloc_strdup(NULL, radlog_dir); if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) { DEBUG("Failed to create logdir %s: %s", my_dir, strerror(errno)); } talloc_free(my_dir); } /* * If we don't already have a log file open, open one * now. We may not have been logging anything yet. The * server normally starts up fairly quietly. */ if ((default_log.dst == L_DST_FILES) && (default_log.fd < 0)) { default_log.fd = open(main_config.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); if (default_log.fd < 0) { fprintf(stderr, "%s: Failed to open log file %s: %s\n", main_config.name, main_config.log_file, fr_syserror(errno)); return 0; } } /* * If we need to change UID, ensure that the log files * have the correct owner && group. * * We have to do this because some log files MAY already * have been written as root. We need to change them to * have the correct ownership before proceeding. */ if ((do_suid || do_sgid) && (default_log.dst == L_DST_FILES)) { if (fchown(default_log.fd, server_uid, server_gid) < 0) { fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n", main_config.name, main_config.log_file, fr_syserror(errno)); return 0; } } /* * Once we're done with all of the privileged work, * permanently change the UID. */ if (do_suid) { rad_suid_set_down_uid(server_uid); rad_suid_down(); } /* * This also clears the dumpable flag if core dumps * aren't allowed. */ if (fr_set_dumpable(allow_core_dumps) < 0) { ERROR("%s", fr_strerror()); } if (allow_core_dumps) { INFO("Core dumps are enabled"); } return 1; }
/** 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; }