Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
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;
}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
0
/* 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
    }
Ejemplo n.º 7
0
Archivo: ctrls.c Proyecto: OPSF/uClinux
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;
}
Ejemplo n.º 8
0
Archivo: ctrls.c Proyecto: OPSF/uClinux
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;
}
Ejemplo n.º 9
0
Archivo: ctrls.c Proyecto: OPSF/uClinux
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;
}
Ejemplo n.º 10
0
Archivo: ctrls.c Proyecto: OPSF/uClinux
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;
}
Ejemplo n.º 11
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;
}
Ejemplo n.º 12
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;
}
Ejemplo n.º 13
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;
}