int pr_scoreboard_add_entry(void) { unsigned char found_slot = FALSE; if (scoreboard_fd < 0) { errno = EINVAL; return -1; } /* Write-lock the scoreboard file. */ if (wlock_scoreboard() < 0) return -1; /* No interruptions, please. */ pr_signals_block(); /* If the scoreboard is open, the file position is already past the * header. */ while (TRUE) { int res = 0; while ((res = read(scoreboard_fd, &entry, sizeof(entry))) == sizeof(entry)) { /* If this entry's PID is marked as zero, it means this slot can be * reused. */ if (!entry.sce_pid) { entry_lock.l_start = lseek(scoreboard_fd, 0, SEEK_CUR) - sizeof(entry); found_slot = TRUE; break; } } if (res == 0) { entry_lock.l_start = lseek(scoreboard_fd, 0, SEEK_CUR); found_slot = TRUE; } if (found_slot) break; } memset(&entry, '\0', sizeof(entry)); entry.sce_pid = getpid(); entry.sce_uid = geteuid(); entry.sce_gid = getegid(); if (write_entry() < 0) pr_log_pri(PR_LOG_NOTICE, "error writing scoreboard entry: %s", strerror(errno)); pr_signals_unblock(); /* We can unlock the scoreboard now. */ unlock_scoreboard(); return 0; }
static int read_scoreboard_header(pr_scoreboard_header_t *sch) { int res = 0; /* No interruptions, please. */ pr_signals_block(); /* NOTE: reading a struct from a file using read(2) -- bad (in general). */ while ((res = read(scoreboard_fd, sch, sizeof(pr_scoreboard_header_t))) != sizeof(pr_scoreboard_header_t)) { int rd_errno = errno; if (res == 0) { pr_signals_unblock(); errno = EIO; return -1; } if (errno == EINTR) { pr_signals_handle(); continue; } pr_signals_unblock(); errno = rd_errno; return -1; } pr_signals_unblock(); /* Note: these errors will most likely occur only for inetd-run daemons. * Standalone daemons erase the scoreboard on startup. */ if (sch->sch_magic != PR_SCOREBOARD_MAGIC) { pr_close_scoreboard(); return PR_SCORE_ERR_BAD_MAGIC; } if (sch->sch_version < PR_SCOREBOARD_VERSION) { pr_close_scoreboard(); return PR_SCORE_ERR_OLDER_VERSION; } if (sch->sch_version > PR_SCOREBOARD_VERSION) { pr_close_scoreboard(); return PR_SCORE_ERR_NEWER_VERSION; } return 0; }
int pr_timer_usleep(unsigned long usecs) { struct timeval tv; if (usecs == 0) { errno = EINVAL; return -1; } tv.tv_sec = (usecs / 1000000); tv.tv_usec = (usecs - (tv.tv_sec * 1000000)); pr_signals_block(); (void) select(0, NULL, NULL, NULL, &tv); pr_signals_unblock(); return 0; }
int pr_trace_set_file(const char *path) { int res; if (!path) { if (trace_logfd < 0) { errno = EINVAL; return -1; } (void) close(trace_logfd); trace_logfd = -1; return 0; } pr_signals_block(); PRIVS_ROOT res = pr_log_openfile(path, &trace_logfd, 0660); PRIVS_RELINQUISH pr_signals_unblock(); if (res < 0) { if (res == -1) { pr_log_debug(DEBUG1, "unable to open TraceLog '%s': %s", path, strerror(errno)); } else if (res == PR_LOG_WRITABLE_DIR) { pr_log_debug(DEBUG1, "unable to open TraceLog '%s': parent directory is world-writable", path); } else if (res == PR_LOG_SYMLINK) { pr_log_debug(DEBUG1, "unable to open TraceLog '%s': cannot log to a symbolic link", path); } return res; } return 0; }
static int exec_openlog(void) { int res = 0; /* Sanity check */ if ((exec_logname = (char *) get_param_ptr(main_server->conf, "ExecLog", FALSE)) == NULL) return 0; /* Check for "none". */ if (!strcasecmp(exec_logname, "none")) { exec_logname = NULL; return 0; } pr_signals_block(); PRIVS_ROOT res = log_openfile(exec_logname, &exec_logfd, 0640); PRIVS_RELINQUISH pr_signals_unblock(); return res; }
/* Initialize a new connection record, also creates a new subpool just for the * new connection. */ static conn_t *init_conn(pool *p, int fd, pr_netaddr_t *bind_addr, int port, int retry_bind, int reporting) { pool *sub_pool = NULL; conn_t *c; pr_netaddr_t na; int addr_family; int res = 0, one = 1, hold_errno; if (!inet_pool) { inet_pool = make_sub_pool(permanent_pool); pr_pool_tag(inet_pool, "Inet Pool"); } /* Initialize the netaddr. */ pr_netaddr_clear(&na); sub_pool = make_sub_pool(p); pr_pool_tag(sub_pool, "init_conn() subpool"); c = (conn_t *) pcalloc(sub_pool, sizeof(conn_t)); c->pool = sub_pool; c->local_port = port; c->rfd = c->wfd = -1; if (bind_addr) { addr_family = pr_netaddr_get_family(bind_addr); } else if (inet_family) { addr_family = inet_family; } else { /* If no default family has been set, then default to IPv6 (if IPv6 * support is enabled), otherwise use IPv4. */ #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) addr_family = AF_INET6; else addr_family = AF_INET; #else addr_family = AF_INET; #endif /* PR_USE_IPV6 */ } /* If fd == -1, there is no currently open socket, so create one. */ if (fd == -1) { socklen_t salen; register unsigned int i = 0; /* Certain versions of Solaris apparently require us to be root * in order to create a socket inside a chroot. * * FreeBSD 2.2.6 (possibly other versions as well), has a security * "feature" which disallows SO_REUSEADDR from working if the socket * owners don't match. The easiest thing to do is simply make sure * the socket is created as root. (Note: this "feature" seems to apply * to _all_ BSDs.) */ #if defined(SOLARIS2) || defined(FREEBSD2) || defined(FREEBSD3) || \ defined(FREEBSD4) || defined(FREEBSD5) || defined(FREEBSD6) || \ defined(FREEBSD7) || defined(FREEBSD8) || defined(FREEBSD9) || \ defined(__OpenBSD__) || defined(__NetBSD__) || \ defined(DARWIN6) || defined(DARWIN7) || defined(DARWIN8) || \ defined(DARWIN9) || defined(DARWIN10) || defined(DARWIN11) || \ defined(SCO3) || defined(CYGWIN) || defined(SYSV4_2MP) || \ defined(SYSV5SCO_SV6) || defined(SYSV5UNIXWARE7) # ifdef SOLARIS2 if (port != INPORT_ANY && port < 1024) { # endif pr_signals_block(); PRIVS_ROOT # ifdef SOLARIS2 }
int pr_ctrls_connect(const char *socket_file) { int sockfd = -1, len = 0; struct sockaddr_un cl_sock, ctrl_sock; /* No interruptions */ pr_signals_block(); /* Create a Unix domain socket */ sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd < 0) { pr_signals_unblock(); return -1; } /* Fill in the socket address */ memset(&cl_sock, 0, sizeof(cl_sock)); /* This first part is clever. First, this process creates a socket in * the file system. It _then_ connect()s to the server. Upon accept()ing * the connection, the server examines the created socket to see that it * is indeed a socket, with the proper mode and time. Clever, but not * ideal. */ cl_sock.sun_family = AF_UNIX; sprintf(cl_sock.sun_path, "%s%05u", "/tmp/ftp.cl", (unsigned int) getpid()); len = sizeof(cl_sock); /* Make sure the file doesn't already exist */ unlink(cl_sock.sun_path); /* Make it a socket */ if (bind(sockfd, (struct sockaddr *) &cl_sock, len) < 0) { unlink(cl_sock.sun_path); pr_signals_unblock(); return -1; } /* Set the proper mode */ if (chmod(cl_sock.sun_path, PR_CTRLS_CL_MODE) < 0) { unlink(cl_sock.sun_path); pr_signals_unblock(); return -1; } /* Now connect to the real server */ memset(&ctrl_sock, 0, sizeof(ctrl_sock)); ctrl_sock.sun_family = AF_UNIX; strncpy(ctrl_sock.sun_path, socket_file, strlen(socket_file)); len = sizeof(ctrl_sock); if (connect(sockfd, (struct sockaddr *) &ctrl_sock, len) < 0) { unlink(cl_sock.sun_path); pr_signals_unblock(); return -1; } pr_signals_unblock(); return sockfd; }
int pr_ctrls_send_msg(int sockfd, int msgstatus, unsigned int msgargc, char **msgargv) { register int i = 0; unsigned int msgarglen = 0; /* Sanity checks */ if (sockfd < 0) { errno = EINVAL; return -1; } if (msgargc < 1) return 0; if (msgargv == NULL) return 0; /* No interruptions */ pr_signals_block(); /* Send the message status first */ if (write(sockfd, &msgstatus, sizeof(int)) != sizeof(int)) { pr_signals_unblock(); return -1; } /* Send the strings, one argument at a time. First, send the number * of arguments to be sent; then send, for each argument, first the * length of the argument string, then the argument itself. */ if (write(sockfd, &msgargc, sizeof(unsigned int)) != sizeof(unsigned int)) { pr_signals_unblock(); return -1; } for (i = 0; i < msgargc; i++) { int res = 0; msgarglen = strlen(msgargv[i]); while (TRUE) { res = write(sockfd, &msgarglen, sizeof(unsigned int)); if (res != sizeof(unsigned int)) { if (errno == EAGAIN) continue; pr_signals_unblock(); return -1; } else break; } while (TRUE) { res = write(sockfd, msgargv[i], msgarglen); if (res != msgarglen) { if (errno == EAGAIN) continue; pr_signals_unblock(); return -1; } else break; } } pr_signals_unblock(); return 0; }
int pr_ctrls_recv_response(pool *resp_pool, int ctrls_sockfd, int *status, char ***respargv) { register int i = 0; array_header *resparr = NULL; unsigned int respargc = 0, resparglen = 0; char response[PR_TUNABLE_BUFFER_SIZE] = {'\0'}; /* Sanity checks */ if (!resp_pool || ctrls_sockfd < 0 || !status) { errno = EINVAL; return -1; } resparr = make_array(resp_pool, 0, sizeof(char *)); /* No interruptions. */ pr_signals_block(); /* First, read the status, which is the return value of the control handler. */ if (read(ctrls_sockfd, status, sizeof(int)) != sizeof(int)) { pr_signals_unblock(); return -1; } /* Next, read the number of responses to be received */ if (read(ctrls_sockfd, &respargc, sizeof(unsigned int)) != sizeof(unsigned int)) { pr_signals_unblock(); return -1; } /* Read each response, and add it to the array */ for (i = 0; i < respargc; i++) { int bread = 0, blen = 0; if (read(ctrls_sockfd, &resparglen, sizeof(unsigned int)) != sizeof(unsigned int)) { pr_signals_unblock(); return -1; } memset(response, '\0', sizeof(response)); /* Make sure resparglen is not too big */ if (resparglen > sizeof(response)) { pr_signals_unblock(); errno = ENOMEM; return -1; } bread = read(ctrls_sockfd, response, resparglen); while (bread != resparglen) { if (bread < 0) { pr_signals_unblock(); return -1; } blen += bread; bread = read(ctrls_sockfd, response + blen, resparglen - blen); } /* Always make sure the buffer is zero-terminated */ response[sizeof(response)-1] = '\0'; *((char **) push_array(resparr)) = pstrdup(resp_pool, response); } if (respargv) *respargv = ((char **) resparr->elts); pr_signals_unblock(); return respargc; }
int pr_ctrls_recv_request(pr_ctrls_cl_t *cl) { pr_ctrls_t *ctrl = NULL, *next_ctrl = NULL; char reqaction[512] = {'\0'}, *reqarg = NULL; size_t reqargsz = 0; unsigned int nreqargs = 0, reqarglen = 0; int status = 0; register int i = 0; if (!cl) { errno = EINVAL; return -1; } if (cl->cl_fd < 0) { errno = EBADF; return -1; } /* No interruptions */ pr_signals_block(); /* Read in the incoming number of args, including the action. */ /* First, read the status (but ignore it). This is necessary because * the same function, pr_ctrls_send_msg(), is used to send requests * as well as responses, and the status is a necessary part of a response. */ if (read(cl->cl_fd, &status, sizeof(int)) < 0) { pr_signals_unblock(); return -1; } /* Read in the args, length first, then string. */ if (read(cl->cl_fd, &nreqargs, sizeof(unsigned int)) < 0) { pr_signals_unblock(); return -1; } /* Next, read in the requested number of arguments. The client sends * the arguments in pairs: first the length of the argument, then the * argument itself. The first argument is the action, so get the first * matching pr_ctrls_t (if present), and add the remaining arguments to it. */ if (read(cl->cl_fd, &reqarglen, sizeof(unsigned int)) < 0) { pr_signals_unblock(); return -1; } if (read(cl->cl_fd, reqaction, reqarglen) < 0) { pr_signals_unblock(); return -1; } nreqargs--; /* Find a matching action object, and use it to populate a ctrl object, * preparing the ctrl object for dispatching to the action handlers. */ ctrl = ctrls_lookup_action(NULL, reqaction, TRUE); if (ctrl == NULL) { pr_signals_unblock(); errno = EINVAL; return -1; } for (i = 0; i < nreqargs; i++) { memset(reqarg, '\0', reqargsz); if (read(cl->cl_fd, &reqarglen, sizeof(unsigned int)) < 0) { pr_signals_unblock(); return -1; } /* Make sure reqarg is large enough to handle the given argument. If * it is too small, allocate one of the necessary size. */ if (reqargsz < reqarglen) { reqargsz = reqarglen + 1; if (!ctrl->ctrls_tmp_pool) { ctrl->ctrls_tmp_pool = make_sub_pool(ctrls_pool); pr_pool_tag(ctrl->ctrls_tmp_pool, "ctrls tmp pool"); } reqarg = pcalloc(ctrl->ctrls_tmp_pool, reqargsz); } if (read(cl->cl_fd, reqarg, reqarglen) < 0) { pr_signals_unblock(); return -1; } if (pr_ctrls_add_arg(ctrl, reqarg)) { pr_signals_unblock(); return -1; } } /* Add this ctrls object to the client object. */ *((pr_ctrls_t **) push_array(cl->cl_ctrls)) = ctrl; /* Set the flag that this control is ready to go */ ctrl->ctrls_flags |= PR_CTRLS_REQUESTED; ctrl->ctrls_cl = cl; /* Copy the populated ctrl object args to ctrl objects for all other * matching action objects. */ next_ctrl = ctrls_lookup_next_action(NULL, TRUE); while (next_ctrl) { if (pr_ctrls_copy_args(ctrl, next_ctrl)) return -1; /* Add this ctrl object to the client object. */ *((pr_ctrls_t **) push_array(cl->cl_ctrls)) = next_ctrl; /* Set the flag that this control is ready to go. */ next_ctrl->ctrls_flags |= PR_CTRLS_REQUESTED; next_ctrl->ctrls_cl = cl; next_ctrl = ctrls_lookup_next_action(NULL, TRUE); } pr_signals_unblock(); return 0; }
static int mcache_sess_init(void) { config_rec *c; pr_event_register(&memcache_module, "core.session-reinit", mcache_sess_reinit_ev, NULL); c = find_config(main_server->conf, CONF_PARAM, "MemcacheEngine", FALSE); if (c) { int engine; engine = *((int *) c->argv[0]); if (engine == FALSE) { /* Explicitly disable memcache support for this session */ memcache_set_servers(NULL); return 0; } } pr_event_register(&memcache_module, "core.exit", mcache_exit_ev, NULL); c = find_config(main_server->conf, CONF_PARAM, "MemcacheLog", FALSE); if (c) { const char *path; path = c->argv[0]; if (strcasecmp(path, "none") != 0) { int res, xerrno; pr_signals_block(); PRIVS_ROOT res = pr_log_openfile(path, &memcache_logfd, PR_LOG_SYSTEM_MODE); xerrno = errno; PRIVS_RELINQUISH pr_signals_unblock(); switch (res) { case 0: break; case -1: pr_log_pri(PR_LOG_NOTICE, MOD_MEMCACHE_VERSION ": notice: unable to open MemcacheLog '%s': %s", path, strerror(xerrno)); break; case PR_LOG_WRITABLE_DIR: pr_log_pri(PR_LOG_WARNING, MOD_MEMCACHE_VERSION ": notice: unable to use MemcacheLog '%s': parent directory is " "world-writable", path); break; case PR_LOG_SYMLINK: pr_log_pri(PR_LOG_WARNING, MOD_MEMCACHE_VERSION ": notice: unable to use MemcacheLog '%s': cannot log to a symlink", path); break; } } } c = find_config(main_server->conf, CONF_PARAM, "MemcacheServers", FALSE); if (c) { memcached_server_st *memcache_servers; memcache_servers = c->argv[0]; memcache_set_servers(memcache_servers); } c = find_config(main_server->conf, CONF_PARAM, "MemcacheOptions", FALSE); if (c) { unsigned long flags; flags = *((unsigned long *) c->argv[0]); if (memcache_set_sess_flags(flags) < 0) { (void) pr_log_writefile(memcache_logfd, MOD_MEMCACHE_VERSION, "error setting memcache flags: %s", strerror(errno)); } } c = find_config(main_server->conf, CONF_PARAM, "MemcacheReplicas", FALSE); if (c) { uint64_t count; count = *((uint64_t *) c->argv[0]); if (memcache_set_sess_replicas(count) < 0) { (void) pr_log_writefile(memcache_logfd, MOD_MEMCACHE_VERSION, "error setting memcache replicas: %s", strerror(errno)); } } c = find_config(main_server->conf, CONF_PARAM, "MemcacheTimeouts", FALSE); if (c) { unsigned long conn_millis, read_millis, write_millis, retry_sec; conn_millis = *((unsigned long *) c->argv[0]); read_millis = *((unsigned long *) c->argv[1]); write_millis = *((unsigned long *) c->argv[2]); retry_sec = *((unsigned long *) c->argv[3]); if (memcache_set_timeouts(conn_millis, read_millis, write_millis, retry_sec) < 0) { (void) pr_log_writefile(memcache_logfd, MOD_MEMCACHE_VERSION, "error setting memcache timeouts: %s", strerror(errno)); } } return 0; }
static int sftppam_driver_authenticate(sftp_kbdint_driver_t *driver, const char *user) { int res; pr_signals_block(); PRIVS_ROOT res = pam_authenticate(sftppam_pamh, 0); if (res != PAM_SUCCESS) { switch (res) { case PAM_USER_UNKNOWN: sftppam_auth_code = PR_AUTH_NOPWD; break; default: sftppam_auth_code = PR_AUTH_BADPWD; } (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, "PAM authentication error (%d) for user '%s': %s", res, user, pam_strerror(sftppam_pamh, res)); (void) pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION ": PAM authentication error (%d) for user '%s': %s", res, user, pam_strerror(sftppam_pamh, res)); PRIVS_RELINQUISH pr_signals_unblock(); errno = EPERM; return -1; } res = pam_acct_mgmt(sftppam_pamh, 0); if (res != PAM_SUCCESS) { switch (res) { #ifdef PAM_AUTHTOKEN_REQD case PAM_AUTHTOKEN_REQD: pr_trace_msg(trace_channel, 8, "PAM account mgmt error: PAM_AUTHTOKEN_REQD"); break; #endif case PAM_ACCT_EXPIRED: pr_trace_msg(trace_channel, 8, "PAM account mgmt error: PAM_ACCT_EXPIRED"); sftppam_auth_code = PR_AUTH_DISABLEDPWD; break; #ifdef PAM_ACCT_DISABLED case PAM_ACCT_DISABLED: pr_trace_msg(trace_channel, 8, "PAM account mgmt error: PAM_ACCT_DISABLED"); sftppam_auth_code = PR_AUTH_DISABLEDPWD; break; #endif case PAM_USER_UNKNOWN: pr_trace_msg(trace_channel, 8, "PAM account mgmt error: PAM_USER_UNKNOWN"); sftppam_auth_code = PR_AUTH_NOPWD; break; default: sftppam_auth_code = PR_AUTH_BADPWD; break; } pr_trace_msg(trace_channel, 1, "PAM account mgmt error (%d) for user '%s': %s", res, user, pam_strerror(sftppam_pamh, res)); PRIVS_RELINQUISH pr_signals_unblock(); errno = EPERM; return -1; } res = pam_open_session(sftppam_pamh, 0); if (res != PAM_SUCCESS) { sftppam_auth_code = PR_AUTH_DISABLEDPWD; pr_trace_msg(trace_channel, 1, "PAM session error (%d) for user '%s': %s", res, user, pam_strerror(sftppam_pamh, res)); PRIVS_RELINQUISH pr_signals_unblock(); errno = EPERM; return -1; } #ifdef PAM_CRED_ESTABLISH res = pam_setcred(sftppam_pamh, PAM_CRED_ESTABLISH); #else res = pam_setcred(sftppam_pamh, PAM_ESTABLISH_CRED); #endif /* !PAM_CRED_ESTABLISH */ if (res != PAM_SUCCESS) { switch (res) { case PAM_CRED_EXPIRED: pr_trace_msg(trace_channel, 8, "PAM credentials error: PAM_CRED_EXPIRED"); sftppam_auth_code = PR_AUTH_AGEPWD; break; case PAM_USER_UNKNOWN: pr_trace_msg(trace_channel, 8, "PAM credentials error: PAM_USER_UNKNOWN"); sftppam_auth_code = PR_AUTH_NOPWD; break; default: sftppam_auth_code = PR_AUTH_BADPWD; break; } pr_trace_msg(trace_channel, 1, "PAM credentials error (%d) for user '%s': %s", res, user, pam_strerror(sftppam_pamh, res)); PRIVS_RELINQUISH pr_signals_unblock(); errno = EPERM; return -1; } /* XXX Not sure why these platforms have different treatment...? */ #if defined(SOLARIS2) || defined(HPUX10) || defined(HPUX11) res = pam_close_session(sftppam_pamh, 0); if (sftppam_pamh) { pam_end(sftppam_pamh, res); sftppam_pamh = NULL; } #endif PRIVS_RELINQUISH pr_signals_unblock(); return 0; }
static int sftppam_driver_open(sftp_kbdint_driver_t *driver, const char *user) { int res; config_rec *c; /* XXX Should we pay attention to AuthOrder here? I.e. if AuthOrder * does not include mod_sftp_pam or mod_auth_pam, should we fail to * open this driver, since the AuthOrder indicates that no PAM check is * desired? For this to work, AuthOrder needs to have been processed * prior to this callback being invoked... */ /* Figure out our default return style: whether or not PAM should allow * other auth modules a shot at this user or not is controlled by adding * '*' to a module name in the AuthOrder directive. By default, auth * modules are not authoritative, and allow other auth modules a chance at * authenticating the user. This is not the most secure configuration, but * it allows things like AuthUserFile to work "out of the box". */ if (sftppam_authtab[0].auth_flags & PR_AUTH_FL_REQUIRED) { sftppam_authoritative = TRUE; } sftppam_userlen = strlen(user) + 1; if (sftppam_userlen > (PAM_MAX_MSG_SIZE + 1)) { sftppam_userlen = PAM_MAX_MSG_SIZE + 1; } #ifdef MAXLOGNAME /* Some platforms' PAM libraries do not handle login strings that exceed * this length. */ if (sftppam_userlen > MAXLOGNAME) { pr_log_pri(PR_LOG_NOTICE, "PAM(%s): Name exceeds maximum login length (%u)", user, MAXLOGNAME); pr_trace_msg(trace_channel, 1, "user name '%s' exceeds maximum login length %u, declining", user, MAXLOGNAME); errno = EPERM; return -1; } #endif sftppam_user = malloc(sftppam_userlen); if (sftppam_user == NULL) { pr_log_pri(PR_LOG_ALERT, MOD_SFTP_PAM_VERSION ": Out of memory!"); exit(1); } memset(sftppam_user, '\0', sftppam_userlen); sstrncpy(sftppam_user, user, sftppam_userlen); c = find_config(main_server->conf, CONF_PARAM, "SFTPPAMOptions", FALSE); while (c != NULL) { unsigned long opts; pr_signals_handle(); opts = *((unsigned long *) c->argv[0]); sftppam_opts |= opts; c = find_config_next(c, c->next, CONF_PARAM, "SFTPPAMOptions", FALSE); } #ifdef SOLARIS2 /* For Solaris environments, the TTY environment will always be set, * in order to workaround a bug (Solaris Bug ID 4250887) where * pam_open_session() will crash unless both PAM_RHOST and PAM_TTY are * set, and the PAM_TTY setting is at least greater than the length of * the string "/dev/". */ sftppam_opts &= ~SFTP_PAM_OPT_NO_TTY; #endif /* SOLARIS2 */ pr_signals_block(); PRIVS_ROOT res = pam_start(sftppam_service, sftppam_user, &sftppam_conv, &sftppam_pamh); if (res != PAM_SUCCESS) { PRIVS_RELINQUISH pr_signals_unblock(); free(sftppam_user); sftppam_user = NULL; sftppam_userlen = 0; switch (res) { case PAM_SYSTEM_ERR: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, "error starting PAM service: %s", strerror(errno)); break; case PAM_BUF_ERR: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, "error starting PAM service: Memory buffer error"); break; } return -1; } pam_set_item(sftppam_pamh, PAM_RUSER, sftppam_user); pam_set_item(sftppam_pamh, PAM_RHOST, session.c->remote_name); if (!(sftppam_opts & SFTP_PAM_OPT_NO_TTY)) { memset(sftppam_tty, '\0', sizeof(sftppam_tty)); snprintf(sftppam_tty, sizeof(sftppam_tty), "/dev/ftpd%02lu", (unsigned long) (session.pid ? session.pid : getpid())); sftppam_tty[sizeof(sftppam_tty)-1] = '\0'; pr_trace_msg(trace_channel, 9, "setting PAM_TTY to '%s'", sftppam_tty); pam_set_item(sftppam_pamh, PAM_TTY, sftppam_tty); } PRIVS_RELINQUISH pr_signals_unblock(); /* We need to disable mod_auth_pam, since both mod_auth_pam and us want * to talk to the PAM API, just in different fashions. */ c = add_config_param_set(&(main_server->conf), "AuthPAM", 1, NULL); c->argv[0] = palloc(c->pool, sizeof(unsigned char)); *((unsigned char *) c->argv[0]) = FALSE; if (pr_auth_remove_auth_only_module("mod_auth_pam.c") < 0) { if (errno != ENOENT) { pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION ": error removing 'mod_auth_pam.c' from the auth-only module list: %s", strerror(errno)); } } if (pr_auth_add_auth_only_module("mod_sftp_pam.c") < 0) { if (errno != EEXIST) { pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION ": error adding 'mod_sftp_pam.c' to the auth-only module list: %s", strerror(errno)); } } sftppam_handle_auth = TRUE; driver->driver_pool = make_sub_pool(permanent_pool); pr_pool_tag(driver->driver_pool, "PAM keyboard-interactive driver pool"); return 0; }