Beispiel #1
0
int pr_memcache_conn_set_namespace(pr_memcache_t *mcache, module *m,
    const char *prefix) {

  if (mcache == NULL ||
      m == NULL) {
    errno = EINVAL;
    return -1;
  }

  if (mcache->namespace_tab == NULL) {
    pr_table_t *tab;

    tab = pr_table_alloc(mcache->pool, 0);

    if (pr_table_ctl(tab, PR_TABLE_CTL_SET_KEY_CMP, modptr_cmp_cb) < 0) {
      pr_trace_msg(trace_channel, 4,
        "error setting key comparison callback for namespace table: %s",
        strerror(errno));
    }

    if (pr_table_ctl(tab, PR_TABLE_CTL_SET_KEY_HASH, modptr_hash_cb) < 0) {
      pr_trace_msg(trace_channel, 4,
        "error setting key hash callback for namespace table: %s",
        strerror(errno));
    }

    mcache->namespace_tab = tab;
  }

  if (prefix != NULL) {
    int count;
    size_t prefix_len;

    prefix_len = strlen(prefix);

    count = pr_table_kexists(mcache->namespace_tab, m, sizeof(module *));
    if (count <= 0) {
      if (pr_table_kadd(mcache->namespace_tab, m, sizeof(module *),
          pstrndup(mcache->pool, prefix, prefix_len), prefix_len) < 0) {
        pr_trace_msg(trace_channel, 7,
          "error adding namespace prefix '%s' for module 'mod_%s.c': %s",
          prefix, m->name, strerror(errno));
      }

    } else {
      if (pr_table_kset(mcache->namespace_tab, m, sizeof(module *),
          pstrndup(mcache->pool, prefix, prefix_len), prefix_len) < 0) {
        pr_trace_msg(trace_channel, 7,
          "error setting namespace prefix '%s' for module 'mod_%s.c': %s",
          prefix, m->name, strerror(errno));
      }
    }

  } else {
    /* A NULL prefix means the caller is removing their namespace maping. */
    (void) pr_table_kremove(mcache->namespace_tab, m, sizeof(module *), NULL);
  }

  return 0;
}
Beispiel #2
0
static char *get_config_word(pool *p, char *word) {
  size_t wordlen;

  /* Should this word be replaced with a value from the environment?
   * If so, tmp will contain the expanded value, otherwise tmp will
   * contain a string duped from the given pool.
   */

  wordlen = strlen(word);

  if (wordlen > 7) {
    char *ptr = NULL;

    /* Does the given word use the environment syntax? We handle this in a
     * while loop in order to handle a) multiple different variables, and b)
     * cases where the substituted value is itself a variable.  Hopefully no
     * one is so clever as to want to actually _use_ the latter approach.
     */
    ptr = strstr(word, "%{env:");
    while (ptr != NULL) {
      char *env, *key, *ptr2, *var;
      unsigned int keylen;

      pr_signals_handle();

      ptr2 = strchr(ptr + 6, '}');
      if (ptr2 == NULL) {
        /* No terminating marker; continue on to the next potential
         * variable in the word.
         */
        ptr2 = ptr + 6;
        ptr = strstr(ptr2, "%{env:");
        continue;
      }

      keylen = (ptr2 - ptr - 6);
      var = pstrndup(p, ptr, (ptr2 - ptr) + 1);

      key = pstrndup(p, ptr + 6, keylen);

      env = pr_env_get(p, key);
      if (env == NULL) {
        /* No value in the environment; continue on to the next potential
         * variable in the word.
         */
        ptr = strstr(ptr2, "%{env:");
        continue;
      }

      word = (char *) sreplace(p, word, var, env, NULL);
      ptr = strstr(word, "%{env:");
    }
  }

  return pstrdup(p, word);
}
Beispiel #3
0
/* Many FTP servers (e.g. IIS) use the semicolon delimiter syntax, as used
 * for listing the MLSD/MLST facts, for other FEAT values (e.g. AUTH, PROT,
 * etc).
 *
 * NOTE: Should this return a table rather than an array, for easier lookup
 * of parsed values by callers?
 */
static int parse_feat(pool *p, const char *feat, array_header **res) {
    char *ptr, *ptr2 = NULL;
    array_header *vals;
    size_t len;

    vals = make_array(p, 1, sizeof(char *));

    /* No semicolons in this value?  No work to do...*/
    ptr = strchr(feat, ';');
    if (ptr == NULL) {
        *((char **) push_array(vals)) = pstrdup(p, feat);
        *res = vals;
        return vals->nelts;
    }

    len = ptr - feat;
    if (len > 0) {
        *((char **) push_array(vals)) = pstrndup(p, feat, len);
    }

    /* Watch for any sequences of just semicolons. */
    while (*ptr == ';') {
        pr_signals_handle();
        ptr++;
    }

    ptr2 = strchr(ptr, ';');
    while (ptr2 != NULL) {
        pr_signals_handle();

        len = ptr2 - ptr;
        if (len > 0) {
            *((char **) push_array(vals)) = pstrndup(p, ptr, len);
        }

        ptr = ptr2;
        while (*ptr == ';') {
            pr_signals_handle();
            ptr++;
        }

        ptr2 = strchr(ptr, ';');
    }

    /* Since the semicolon delimiter syntax uses a trailing semicolon,
     * we shouldn't need to worry about something like "...;FOO".  Right?
     */

    *res = vals;
    return vals->nelts;
}
Beispiel #4
0
static const char *generate_secret(pool *p) {
  int res;
  unsigned char encoded[18], rnd[32];
  size_t encoded_len;
  const char *secret;
  const unsigned char *ptr;

  if (RAND_bytes(rnd, sizeof(rnd)) != 1) {
    fprintf(stderr, "Error obtaining %lu bytes of random data:\n",
      (unsigned long) sizeof(rnd));
    ERR_print_errors_fp(stderr);
    errno = EPERM;
    return NULL;
  }

  encoded_len = sizeof(encoded);
  ptr = encoded;
  res = auth_otp_base32_encode(p, rnd, sizeof(rnd), &ptr, &encoded_len);
  if (res < 0) {
    return NULL;
  }

  secret = pstrndup(p, (const char *) ptr, encoded_len);
  return secret;
}
Beispiel #5
0
/************************************************************************ 
** definstall - Install a new definition. id is in Reuse_1.
**	p_text : ptr to the definition
**	n : number of bytes in the definition (may contain embedded nulls)
**	number : number of formals
************************************************************************/
void   definstall(ptext_t p_text, int n, int number)
{
    pdefn_t	p;

    if(n == 0) {
	p_text = NULL;
    }
    if( strcmp (Reuse_1, "defined") == 0) {
	Msg_Temp = GET_MSG (4112);
        SET_MSG (Msg_Text, Msg_Temp, Reuse_1, "#define");
	warning(4112);/* name reserved */
	return;
    }
    if((p = get_defined()) != 0) {
	if(PRE_DEFINED(p)) {
	    Msg_Temp = GET_MSG (4112);
            SET_MSG (Msg_Text, Msg_Temp, Reuse_1, "#define");
	    warning(4112);/* name reserved */
	    return;
	}
	else {
	    if(redefn(p_text, DEFN_TEXT(p), n)) {
		Msg_Temp = GET_MSG (4005);
                SET_MSG (Msg_Text, Msg_Temp, Reuse_1);
		warning(4005);/* redefinition */
	    }
	    else {
		return;
	    }
	}
    }
    else {
	hln_t	ident;

	HLN_NAME(ident) = Reuse_1;
	HLN_HASH(ident) = Reuse_1_hash;
	HLN_LENGTH(ident) = (UCHAR)Reuse_1_length;
	p = malloc(sizeof(defn_t));
	if (p == NULL) {
	    Msg_Temp = GET_MSG (1002);
	    SET_MSG (Msg_Text, Msg_Temp);
	    error(1002);
	    return;
	}
	DEFN_IDENT(p) = HLN_TO_NAME(&ident);
	DEFN_NEXT(p) = Defn_level_0[Reuse_1_hash & LEVEL_0];
	DEFN_TEXT(p) = (char*)NULL;
	DEFN_EXPANDING(p) = 0;
	Defn_level_0[Reuse_1_hash & LEVEL_0] = p;
    }
    if(n != 0) {
	DEFN_TEXT(p) = pstrndup(p_text, n);
	if(number == FROM_COMMAND) {	/* special case from cmd line */
	    *(DEFN_TEXT(p) + n - 1) = EOS_DEFINITION;	/* for handle_eos */
	}
    }
    DEFN_NFORMALS(p) = (char)((number != FROM_COMMAND) ? number : 0);
}
Beispiel #6
0
static int af_check_parent_dir(pool *p, const char *name, const char *path) {
  struct stat st;
  int res;
  char *dir_path, *ptr = NULL;

  ptr = strrchr(path, '/');
  if (ptr != path) {
    dir_path = pstrndup(p, path, ptr - path);

  } else {
    dir_path = "/";
  }

  res = stat(dir_path, &st);
  if (res < 0) {
    int xerrno = errno;

    pr_log_debug(DEBUG0, MOD_AUTH_FILE_VERSION
      ": unable to stat %s directory '%s': %s", name, dir_path,
      strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  if (st.st_mode & S_IWOTH) {
    int xerrno = EPERM;

    pr_log_debug(DEBUG0, MOD_AUTH_FILE_VERSION
      ": unable to use %s from world-writable directory '%s' (perms %04o): %s",
      name, dir_path, st.st_mode & ~S_IFMT, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  return 0;
}
Beispiel #7
0
void xtree_nput(xtree_t xt, void *value, int vlen, const char *key, int len)
{
	node_t node, *pnode;

	if (xt == NULL || key == NULL || len == 0)
		return;

	if ((node = _xtree_node_find(xt, &pnode, key, len)) != NULL)
	{
		node->value = value;
		node->vlen = vlen;
		return;
	}
	
	if (value != NULL)
	{	
		*pnode = node = (node_t) pmalloc(xt->p, sizeof(node_st));
		node->key = pstrndup(xt->p, key, len);
		node->value = value;
		node->vlen = vlen;
		node->left = NULL;
		node->right = NULL;
	}
}
Beispiel #8
0
static int parse_wildcard_config_path(pool *p, const char *path,
    unsigned int depth) {
  register unsigned int i;
  int res, xerrno;
  pool *tmp_pool;
  array_header *globbed_dirs = NULL;
  const char *component = NULL, *parent_path = NULL, *suffix_path = NULL;
  struct stat st;
  size_t path_len, component_len;
  char *name_pattern = NULL;
  void *dirh = NULL;
  struct dirent *dent = NULL;

  if (depth > PR_PARSER_INCLUDE_MAX_DEPTH) {
    pr_log_pri(PR_LOG_WARNING, "error: resolving wildcard pattern in '%s' "
      "exceeded maximum filesystem depth (%u)", path,
      (unsigned int) PR_PARSER_INCLUDE_MAX_DEPTH);
    errno = EINVAL;
    return -1;
  }

  path_len = strlen(path);
  if (path_len < 2) {
    pr_trace_msg(trace_channel, 7, "path '%s' too short to be wildcard path",
      path);

    /* The first character must be a slash, and we need at least one more
     * character in the path as a glob character.
     */
    errno = EINVAL;
    return -1;
  }

  tmp_pool = make_sub_pool(p);
  pr_pool_tag(tmp_pool, "Include sub-pool");

  /* We need to find the first component of the path which contains glob
   * characters.  We then use the path up to the previous component as the
   * parent directory to open, and the glob-bearing component as the filter
   * for directories within the parent.
   */

  component = path + 1;
  while (TRUE) {
    int last_component = FALSE;
    char *ptr;

    pr_signals_handle();

    ptr = strchr(component, '/');
    if (ptr != NULL) {
      component_len = ptr - component;

    } else {
      component_len = strlen(component);
      last_component = TRUE;
    }

    if (memchr(component, (int) '*', component_len) != NULL ||
        memchr(component, (int) '?', component_len) != NULL ||
        memchr(component, (int) '[', component_len) != NULL) {

      name_pattern = pstrndup(tmp_pool, component, component_len);

      if (parent_path == NULL) {
        parent_path = pstrndup(tmp_pool, "/", 1);
      }

      if (ptr != NULL) {
        suffix_path = pstrdup(tmp_pool, ptr + 1);
      }

      break;
    }

    if (parent_path != NULL) {
      parent_path = pdircat(tmp_pool, parent_path,
        pstrndup(tmp_pool, component, component_len), NULL);

    } else {
      parent_path = pstrndup(tmp_pool, "/", 1);
    }

    if (last_component) {
      break;
    }

    component = ptr + 1;
  }

  if (name_pattern == NULL) {
    pr_trace_msg(trace_channel, 4,
      "unable to process invalid, non-globbed path '%s'", path);
    errno = ENOENT;
    return -1;
  }

  pr_fs_clear_cache2(parent_path);
  if (pr_fsio_lstat(parent_path, &st) < 0) {
    xerrno = errno;

    pr_log_pri(PR_LOG_WARNING,
      "error: failed to check configuration path '%s': %s", parent_path,
      strerror(xerrno));

    destroy_pool(tmp_pool);
    errno = xerrno;
    return -1;
  }

  if (S_ISLNK(st.st_mode) &&
      !(parser_include_opts & PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS)) {
    pr_log_pri(PR_LOG_WARNING,
      "error: cannot read configuration path '%s': Symbolic link", parent_path);
    destroy_pool(tmp_pool);
    errno = ENOTDIR;
    return -1;
  }

  pr_log_pri(PR_LOG_DEBUG,
    "processing configuration directory '%s' using pattern '%s', suffix '%s'",
    parent_path, name_pattern, suffix_path);

  dirh = pr_fsio_opendir(parent_path);
  if (dirh == NULL) {
    pr_log_pri(PR_LOG_WARNING,
      "error: unable to open configuration directory '%s': %s", parent_path,
      strerror(errno));
    destroy_pool(tmp_pool);
    errno = EINVAL;
    return -1;
  }

  globbed_dirs = make_array(tmp_pool, 0, sizeof(char *));

  while ((dent = pr_fsio_readdir(dirh)) != NULL) {
    pr_signals_handle();

    if (strncmp(dent->d_name, ".", 2) == 0 ||
        strncmp(dent->d_name, "..", 3) == 0) {
      continue;
    }

    if (parser_include_opts & PR_PARSER_INCLUDE_OPT_IGNORE_TMP_FILES) {
      if (is_tmp_file(dent->d_name) == TRUE) {
        pr_trace_msg(trace_channel, 19,
          "ignoring temporary file '%s' found in directory '%s'", dent->d_name,
          parent_path);
        continue;
      }
    }

    if (pr_fnmatch(name_pattern, dent->d_name, PR_FNM_PERIOD) == 0) {
      pr_trace_msg(trace_channel, 17,
        "matched '%s' path with wildcard pattern '%s'", dent->d_name,
        name_pattern);

      *((char **) push_array(globbed_dirs)) = pdircat(tmp_pool, parent_path,
        dent->d_name, suffix_path, NULL);
    }
  }

  pr_fsio_closedir(dirh);

  if (globbed_dirs->nelts == 0) {
    pr_log_pri(PR_LOG_WARNING,
      "error: no matches found for wildcard directory '%s'", path);
    destroy_pool(tmp_pool);
    errno = ENOENT;
    return -1;
  }

  depth++;

  qsort((void *) globbed_dirs->elts, globbed_dirs->nelts, sizeof(char *),
    config_filename_cmp);

  for (i = 0; i < globbed_dirs->nelts; i++) {
    const char *globbed_dir;

    globbed_dir = ((const char **) globbed_dirs->elts)[i];
    res = parse_config_path2(p, globbed_dir, depth);
    if (res < 0) {
      xerrno = errno;

      pr_trace_msg(trace_channel, 7, "error parsing wildcard path '%s': %s",
        globbed_dir, strerror(xerrno));

      destroy_pool(tmp_pool);
      errno = xerrno;
      return -1;
    }
  }

  destroy_pool(tmp_pool);
  return 0;
}
Beispiel #9
0
static char *get_config_word(pool *p, char *word) {
  size_t wordlen;

  /* Should this word be replaced with a value from the environment?
   * If so, tmp will contain the expanded value, otherwise tmp will
   * contain a string duped from the given pool.
   */

  wordlen = strlen(word);

  if (wordlen > 7) {
    char *ptr = NULL;

    /* Does the given word use the environment syntax?
     *
     * In the simple (and most common) case, the entire word is the variable
     * syntax.  But we also need to check for cases where the environment
     * variable syntax is embedded within the word string.
     */

    if (strncmp(word, "%{env:", 6) == 0 &&
        word[wordlen-1] == '}') {
      char *env;

      word[wordlen-1] = '\0';

      env = pr_env_get(p, word + 6);

      return env ? pstrdup(p, env) : "";
    }

    /* This is in a while loop in order to handle a) multiple different
     * variables, and b) cases where the substituted value is itself a
     * variable.   (Hopefully no one is so clever as to want to actually
     * _use_ the latter approach.)
     */
    ptr = strstr(word, "%{env:");
    while (ptr != NULL) {
      char *env, *key, *ptr2, *var;
      unsigned int keylen;

      pr_signals_handle();

      ptr2 = strchr(ptr + 6, '}');
      if (ptr2 == NULL) {
        /* No terminating marker; continue on to the next potential
         * variable in the word.
         */
        ptr2 = ptr + 6;
        ptr = strstr(ptr2, "%{env:");
        continue;
      }

      keylen = (ptr2 - ptr - 6);
      var = pstrndup(p, ptr, (ptr2 - ptr) + 1);

      key = pstrndup(p, ptr + 6, keylen);

      env = pr_env_get(p, key);
      if (env == NULL) {
        /* No value in the environment; continue on to the next potential
         * variable in the word.
         */
        ptr = strstr(ptr2, "%{env:");
        continue;
      }

      word = (char *) sreplace(p, word, var, env, NULL);
      ptr = strstr(word, "%{env:");
    }
  }

  return pstrdup(p, word);
}
static int counter_sess_init(void) {
  config_rec *c;

  c = find_config(main_server->conf, CONF_PARAM, "CounterEngine", FALSE);
  if (c != NULL) {
    counter_engine = *((int *) c->argv[0]);
  }

  if (counter_engine == FALSE) {
    return 0;
  }

  c = find_config(main_server->conf, CONF_PARAM, "CounterLog", FALSE);
  if (c != NULL) {
    const char *path = c->argv[0];

    if (strcasecmp(path, "none") != 0) {
      int res, xerrno;

      PRIVS_ROOT
      res = pr_log_openfile(path, &counter_logfd, 0660);
      xerrno = errno;
      PRIVS_RELINQUISH;

      if (res < 0) {
        pr_log_debug(DEBUG2, MOD_COUNTER_VERSION
          ": error opening CounterLog '%s': %s", path, strerror(xerrno));
        counter_logfd = -1;
      }
    }
  }

  /* Find all CounterFile directives for this vhost, and make sure they
   * have open handles.  We need to do this here, and not in a POST_CMD
   * PASS handler because of the need to open handles that may be outside
   * of a chroot.
   */
  c = find_config(main_server->conf, CONF_PARAM, "CounterFile", TRUE);
  while (c != NULL) {
    int xerrno = 0;
    const char *area = NULL, *path;
    pr_fh_t *fh;
    struct counter_fh *cfh;

    pr_signals_handle();

    path = c->argv[0];

    if (c->parent != NULL) {
      if (c->parent->config_type == CONF_ANON ||
          c->parent->config_type == CONF_DIR) {
        area = c->parent->name;

      } else {
        (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
          "unhandled configuration parent type (%d) for CounterFile, skipping",
          c->parent->config_type);
        c = find_config_next(c, c->next, CONF_PARAM, "CounterFile", TRUE);
        continue;
      }

    } else {
      /* Toplevel CounterFile directive, in "server config" or <VirtualHost>
       * sections.
       */
      area = "/";
    }

    PRIVS_ROOT
    fh = pr_fsio_open(path, O_RDWR|O_CREAT);
    xerrno = errno;
    PRIVS_RELINQUISH

    if (fh == NULL) {
      pr_log_debug(DEBUG1, MOD_COUNTER_VERSION
        ": error opening CounterFile '%s': %s", path, strerror(xerrno));
      counter_engine = FALSE;

      if (counter_fhs != NULL) {
        for (cfh = (struct counter_fh *) counter_fhs->xas_list; cfh;
            cfh = cfh->next) {
          (void) pr_fsio_close(cfh->fh);
        }
      }

      return 0;
    }

    (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
      "opened CounterFile '%s'", path);

    if (counter_fhs == NULL) {
      counter_fhs = xaset_create(counter_pool, NULL);
    }

    cfh = pcalloc(counter_pool, sizeof(struct counter_fh));

    /* Ignore any trailing slash. */
    cfh->arealen = strlen(area);
    if (cfh->arealen > 1 &&
        area[cfh->arealen-1] == '/') {
      cfh->arealen--;
    }

    cfh->area = pstrndup(counter_pool, area, cfh->arealen);

    /* Mark any areas that use glob(3) characters. */
    if (strpbrk(cfh->area, "[*?") != NULL) {
      cfh->isglob = TRUE;
    }

    cfh->fh = fh;

    xaset_insert(counter_fhs, (xasetmember_t *) cfh);

    c = find_config_next(c, c->next, CONF_PARAM, "CounterFile", TRUE);
  }

  if (counter_fhs == NULL) {
    (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
      "no CounterFiles configured, disabling module");
    counter_engine = FALSE;
    return 0;
  }

  pr_event_register(&counter_module, "core.exit", counter_exit_ev, NULL);

  /* If mod_vroot is present, we need to do a little more magic to counter
   * the mod_vroot magic.
   */
  if (pr_module_exists("mod_vroot.c") == TRUE) {
    pr_event_register(&counter_module, "core.chroot", counter_chroot_ev, NULL);
  }

  return 0;
}
Beispiel #11
0
int proxy_ftp_sess_get_feat(pool *p, struct proxy_session *proxy_sess) {
    pool *tmp_pool;
    int res, xerrno = 0;
    cmd_rec *cmd;
    pr_response_t *resp;
    unsigned int resp_nlines = 0;
    char *feats, *token;
    size_t token_len = 0;

    tmp_pool = make_sub_pool(p);

    cmd = pr_cmd_alloc(tmp_pool, 1, C_FEAT);
    res = proxy_ftp_ctrl_send_cmd(tmp_pool, proxy_sess->backend_ctrl_conn, cmd);
    if (res < 0) {
        xerrno = errno;

        pr_trace_msg(trace_channel, 4,
                     "error sending %s to backend: %s", (char *) cmd->argv[0],
                     strerror(xerrno));
        destroy_pool(tmp_pool);

        errno = xerrno;
        return -1;
    }

    resp = proxy_ftp_ctrl_recv_resp(tmp_pool, proxy_sess->backend_ctrl_conn,
                                    &resp_nlines);
    if (resp == NULL) {
        xerrno = errno;

        pr_trace_msg(trace_channel, 4,
                     "error receiving %s response from backend: %s", (char *) cmd->argv[0],
                     strerror(xerrno));
        destroy_pool(tmp_pool);

        errno = xerrno;
        return -1;
    }

    if (resp->num[0] != '2') {
        pr_trace_msg(trace_channel, 4,
                     "received unexpected %s response code %s from backend",
                     (char *) cmd->argv[0], resp->num);

        /* Note: If the UseProxyProtocol ProxyOption is enabled, AND if the
         * response message mentions a "PROXY" command, we might read an
         * error response here that is NOT actually for the FEAT command we just
         * sent.
         *
         * A backend FTP server which does not understand the PROXY protocol
         * will treat it as a normal FTP command, and respond.  And that will
         * put us, the client, out of lockstep with the server, for how do we know
         * that we need to read that error response FIRST, then send another
         * command?
         */

        destroy_pool(tmp_pool);
        errno = EPERM;
        return -1;
    }

    proxy_sess->backend_features = pr_table_nalloc(p, 0, 4);

    feats = resp->msg;
    token = pr_str_get_token2(&feats, (char *) feat_crlf, &token_len);
    while (token != NULL) {
        pr_signals_handle();

        if (token_len > 0) {
            /* The FEAT response lines in which we are interested all start with
             * a single space, per RFC spec.  Ignore any other lines.
             */
            if (token[0] == ' ') {
                char *key, *val, *ptr;

                /* Find the next space in the string, to delimit our key/value pairs. */
                ptr = strchr(token + 1, ' ');
                if (ptr != NULL) {
                    key = pstrndup(p, token + 1, ptr - token - 1);
                    val = pstrdup(p, ptr + 1);

                } else {
                    key = pstrdup(p, token + 1);
                    val = pstrdup(p, "");
                }

                pr_table_add(proxy_sess->backend_features, key, val, 0);
            }
        }

        feats = token + token_len + 1;
        token = pr_str_get_token2(&feats, (char *) feat_crlf, &token_len);
    }

    destroy_pool(tmp_pool);
    return 0;
}
Beispiel #12
0
static int forward_cmd_parse_dst(pool *p, const char *arg, char **name,
    struct proxy_conn **pconn) {
  const char *default_proto = NULL, *default_port = NULL, *proto = NULL,
    *port, *uri = NULL;
  char *host = NULL, *hostport = NULL, *host_ptr = NULL, *port_ptr = NULL;

  /* TODO: Revisit theses default once we start supporting other protocols. */
  default_proto = "ftp";
  default_port = "21";

  /* First, look for the optional port. */
  port_ptr = strrchr(arg, ':');
  if (port_ptr == NULL) {
    port = default_port;

  } else {
    char *tmp2 = NULL;
    long num;

    num = strtol(port_ptr+1, &tmp2, 10);

    if (tmp2 && *tmp2) {
      /* Trailing garbage found in port number. */
      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "malformed port number '%s' found in USER '%s', rejecting",
        port_ptr+1, arg);
      errno = EINVAL;
      return -1;
    }

    if (num < 0 ||
        num > 65535) {
      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "invalid port number %ld found in USER '%s', rejecting", num, arg);
      errno = EINVAL;
      return -1;
    }

    port = pstrdup(p, port_ptr + 1);
  }

  /* Find the required '@' delimiter. */
  host_ptr = strrchr(arg, '@');
  if (host_ptr == NULL) {
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "missing required '@' delimiter in USER '%s', rejecting", arg);
    errno = EINVAL;
    return -1;
  }

  if (port_ptr == NULL) {
    host = pstrdup(p, host_ptr + 1);

  } else {
    host = pstrndup(p, host_ptr + 1, (port_ptr - host_ptr - 1));
  }

  *name = pstrndup(p, arg, (host_ptr - arg));
  proto = default_proto;

  hostport = pstrcat(p, host, ":", port, NULL);
  if (forward_dst_filter(p, hostport) < 0) {
    return -1;
  }

  uri = pstrcat(p, proto, "://", hostport, NULL);

  /* Note: We deliberately use proxy_pool, rather than the given pool, here
   * so that the created structure (especially the pr_netaddr_t) are
   * longer-lived.
   */
  *pconn = proxy_conn_create(proxy_pool, uri);
  if (*pconn == NULL) {
    int xerrno = errno;

    pr_trace_msg(trace_channel, 1,
      "error handling URI '%.100s': %s", uri, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  return 0;
}
Beispiel #13
0
static char *uri_parse_host(pool *p, const char *orig_uri,
    const char *uri, char **remaining) {
  char *host = NULL, *ptr = NULL;

  /* We have either of:
   *
   *  host<:...>
   *  [host]<:...>
   *
   * Look for an opening square bracket, to see if we have an IPv6 address
   * in the URI.
   */
  if (uri[0] == '[') {
    ptr = strchr(uri + 1, ']');
    if (ptr == NULL) {
      /* If there is no ']', then it's a badly-formatted URI. */
      pr_trace_msg(trace_channel, 4,
        "badly formatted IPv6 address in host info '%.100s'", orig_uri);
      errno = EINVAL;
      return NULL;
    }

    host = pstrndup(p, uri + 1, ptr - uri - 1);

    if (remaining != NULL) {
      size_t urilen;
      urilen = strlen(ptr);

      if (urilen > 0) {
        *remaining = ptr + 1;

      } else {
        *remaining = NULL;
      }
    }

    pr_trace_msg(trace_channel, 17, "parsed host '%s' out of URI '%s'", host,
      orig_uri);
    return host;
  }

  ptr = strchr(uri + 1, ':');
  if (ptr == NULL) {
    if (remaining != NULL) {
      *remaining = NULL;
    }

    host = pstrdup(p, uri);

    pr_trace_msg(trace_channel, 17, "parsed host '%s' out of URI '%s'", host,
      orig_uri);
    return host;
  }

  if (remaining != NULL) {
    *remaining = ptr;
  }

  host = pstrndup(p, uri, ptr - uri);

  pr_trace_msg(trace_channel, 17, "parsed host '%s' out of URI '%s'", host,
    orig_uri);
  return host;
}
Beispiel #14
0
int proxy_uri_parse(pool *p, const char *uri, char **scheme, char **host,
    unsigned int *port, char **username, char **password) {
  char *ptr, *ptr2;
  size_t idx, len;

  if (uri == NULL ||
      scheme == NULL ||
      host == NULL ||
      port == NULL) {
    errno = EINVAL;
    return -1;
  }

  /* First, look for a ':' */
  ptr = strchr(uri, ':');
  if (ptr == NULL) {
    pr_trace_msg(trace_channel, 4, "missing colon in URI '%.100s'", uri);
    errno = EINVAL;
    return -1;
  }

  len = (ptr - uri);
  *scheme = pstrndup(p, uri, len);

  idx = strspn(*scheme, "abcdefghijklmnopqrstuvwxyz+.-");
  if (idx < len &&
      (*scheme)[idx] != '\0') {
    /* Invalid character in the scheme string, according to RFC 1738 rules. */
    pr_trace_msg(trace_channel, 4,
      "invalid character (%c) at index %lu in scheme '%.100s'", (*scheme)[idx],
      (unsigned long) idx, *scheme);
    errno = EINVAL;
    return -1;
  }

  /* The double-slashes must immediately follow the colon. */
  if (*(ptr + 1) != '/' ||
      *(ptr + 2) != '/') {
    pr_trace_msg(trace_channel, 4,
      "missing required '//' following colon in URI '%.100s'", uri);
    errno = EINVAL;
    return -1;
  }

  ptr += 3;

  if (*ptr == '\0') {
    /* The given URL looked like "scheme://". */
    pr_trace_msg(trace_channel, 4,
      "missing required authority following '//' in URI '%.100s'", uri);
    errno = EINVAL;
    return -1;
  }

  /* Possible URIs at this point:
   *
   *  scheme://host:port/path/...
   *  scheme://host:port/
   *  scheme://host:port
   *  scheme://host
   *  scheme://username:password@host...
   *
   * And, in the case where 'host' is an IPv6 address:
   *
   *  scheme://[host]:port/path/...
   *  scheme://[host]:port/
   *  scheme://[host]:port
   *  scheme://[host]
   *  scheme://username:password@[host]...
   */

  /* We explicitly do NOT support URL-encoded characters in the URIs we
   * will handle.
   */
  ptr2 = strchr(ptr, '%');
  if (ptr2 != NULL) {
    pr_trace_msg(trace_channel, 4,
      "invalid character (%%) at index %ld in scheme-specific info '%.100s'",
      (long) (ptr2 - ptr), ptr);
    errno = EINVAL;
    return -1;
  }

  ptr = uri_parse_userinfo(p, uri, ptr, username, password);

  ptr2 = strchr(ptr, ':');
  if (ptr2 == NULL) {
    *host = uri_parse_host(p, uri, ptr, NULL);

    if (strncmp(*scheme, "ftp", 4) == 0 ||
        strncmp(*scheme, "ftps", 5) == 0) {
      *port = 21;

    } else if (strncmp(*scheme, "sftp", 5) == 0) {
      *port = 22;

    } else {
      pr_trace_msg(trace_channel, 4,
        "unable to determine port for scheme '%.100s'", *scheme);
      errno = EINVAL;
      return -1;
    } 

  } else {
    *host = uri_parse_host(p, uri, ptr, &ptr2);
  }

  /* Optional port field present? */
  if (ptr2 != NULL) {
    ptr2 = strchr(ptr2, ':');
  }

  if (ptr2 == NULL) {
    /* XXX How to configure "implicit" FTPS, if at all? */

    if (strncmp(*scheme, "ftp", 4) == 0 ||
        strncmp(*scheme, "ftps", 5) == 0) {
      *port = 21;

    } else if (strncmp(*scheme, "sftp", 5) == 0) {
      *port = 22;

    } else {
      pr_trace_msg(trace_channel, 4,
        "unable to determine port for scheme '%.100s'", *scheme);
      errno = EINVAL;
      return -1;
    }

  } else {
    register unsigned int i;
    char *ptr3, *portspec;
    size_t portspeclen;

    /* Look for any possible trailing '/'. */
    ptr3 = strchr(ptr2, '/');
    if (ptr3 == NULL) {
      portspec = ptr2 + 1;
      portspeclen = strlen(portspec);

    } else {
      portspeclen = ptr3 - (ptr2 + 1);
      portspec = pstrndup(p, ptr2 + 1, portspeclen);
    }

    /* Ensure that only numeric characters appear in the portspec. */
    for (i = 0; i < portspeclen; i++) {
      if (isdigit((int) portspec[i]) == 0) {
        pr_trace_msg(trace_channel, 4,
          "invalid character (%c) at index %d in port specification '%.100s'",
          portspec[i], i, portspec);
        errno = EINVAL;
        return -1;
      }
    }

    /* The above check will rule out any negative numbers, since it will
     * reject the minus character.  Thus we only need to check for a zero
     * port, or a number that's outside the 1-65535 range.
     */
    *port = atoi(portspec);
    if (*port == 0 ||
        *port >= 65536) {
      pr_trace_msg(trace_channel, 4,
        "port specification '%.100s' yields invalid port number %d",
        portspec, *port);
      errno = EINVAL;
      return -1;
    }
  }

  return 0;
}
Beispiel #15
0
/* Determine whether "username:password@" are present.  If so, then parse it
 * out, and return a pointer to the portion of the URI after the parsed-out
 * userinfo.
 */
static char *uri_parse_userinfo(pool *p, const char *orig_uri,
    const char *uri, char **username, char **password) {
  char *ptr, *ptr2, *rem_uri = NULL, *userinfo, *user = NULL, *passwd = NULL;

  /* We have either:
   *
   *  host<:...>
   *  [host]<:...>
   *
   * thus no user info, OR:
   *
   *  username:password@host...
   *  username:password@[host]...
   *  username:@host...
   *  username:pass@word@host...
   *  [email protected]:pass@word@host...
   *
   * all of which have at least one occurrence of the '@' character.
   */

  ptr = strchr(uri, '@');
  if (ptr == NULL) {
    /* No '@' character at all?  No user info, then. */

    if (username != NULL) {
      *username = NULL;
    }

    if (password != NULL) {
      *password = NULL;
    }

    return pstrdup(p, uri);
  }

  /* To handle the case where the password field might itself contain an
   * '@' character, we first search from the end for '@'.  If found, then we
   * search for '@' from the beginning.  If also found, AND if both ocurrences
   * are the same, then we have a plain "username:password@" string.
   *
   * Note that we can handle '@' characters within passwords (or usernames),
   * but we currently cannot handle ':' characters within usernames.
   */

  ptr2 = strrchr(uri, '@');
  if (ptr2 != NULL) {
    if (ptr != ptr2) {
      /* Use the last found '@' as the delimiter. */
      ptr = ptr2;
    }
  }

  userinfo = pstrndup(p, uri, ptr - uri);
  rem_uri = ptr + 1;

  ptr = strchr(userinfo, ':');
  if (ptr == NULL) {
    pr_trace_msg(trace_channel, 4,
      "badly formatted userinfo '%.100s' (missing ':' character) in "
      "URI '%.100s', ignoring", userinfo, orig_uri);

    if (username != NULL) {
      *username = NULL;
    }

    if (password != NULL) {
      *password = NULL;
    }

    return rem_uri;
  }

  user = pstrndup(p, userinfo, ptr - userinfo);
  if (username != NULL) {
    *username = user;
  }

  /* Watch for empty passwords. */
  if (*(ptr+1) == '\0') {
    passwd = pstrdup(p, "");

  } else {
    passwd = pstrdup(p, ptr + 1);
  }

  if (password != NULL) {
    *password = passwd;
  }

  pr_trace_msg(trace_channel, 17,
    "parsed username '%s', password '%s' out of URI '%s'", user, passwd,
    orig_uri);
  return rem_uri;
}
Beispiel #16
0
const char *sftp_display_fh_get_msg(pool *p, pr_fh_t *fh) {
  struct stat st;
  char buf[PR_TUNABLE_BUFFER_SIZE], *msg = "";
  int len, res;
  unsigned int *current_clients = NULL;
  unsigned int *max_clients = NULL;
  off_t fs_size = 0;
  void *v;
  const char *serverfqdn = main_server->ServerFQDN;
  char *outs, mg_size[12] = {'\0'}, mg_size_units[12] = {'\0'},
    mg_max[12] = "unlimited";
  char mg_class_limit[12] = {'\0'}, mg_cur[12] = {'\0'},
    mg_cur_class[12] = {'\0'};
  const char *mg_time;
  char *rfc1413_ident = NULL, *user = NULL;

  /* Stat the opened file to determine the optimal buffer size for IO. */
  memset(&st, 0, sizeof(st));
  if (pr_fsio_fstat(fh, &st) == 0) {
    fh->fh_iosz = st.st_blksize;
  }

  res = pr_fs_fgetsize(fh->fh_fd, &fs_size);
  if (res < 0 &&
      errno != ENOSYS) {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "error getting filesystem size for '%s': %s", fh->fh_path,
      strerror(errno));
    fs_size = 0;
  }

  snprintf(mg_size, sizeof(mg_size), "%" PR_LU, (pr_off_t) fs_size);
  format_size_str(mg_size_units, sizeof(mg_size_units), fs_size);

  mg_time = pr_strtime(time(NULL));

  max_clients = get_param_ptr(main_server->conf, "MaxClients", FALSE);

  v = pr_table_get(session.notes, "client-count", NULL);
  if (v) {
    current_clients = v;
  }

  snprintf(mg_cur, sizeof(mg_cur), "%u", current_clients ? *current_clients: 1);

  if (session.conn_class != NULL &&
      session.conn_class->cls_name) {
    unsigned int *class_clients = NULL;
    config_rec *maxc = NULL;
    unsigned int maxclients = 0;

    v = pr_table_get(session.notes, "class-client-count", NULL);
    if (v) {
      class_clients = v;
    }

    snprintf(mg_cur_class, sizeof(mg_cur_class), "%u",
      class_clients ? *class_clients : 0);

    /* For the %z variable, first we scan through the MaxClientsPerClass,
     * and use the first applicable one.  If none are found, look for
     * any MaxClients set.
     */

    maxc = find_config(main_server->conf, CONF_PARAM, "MaxClientsPerClass",
      FALSE);

    while (maxc) {
      pr_signals_handle();

      if (strcmp(maxc->argv[0], session.conn_class->cls_name) != 0) {
        maxc = find_config_next(maxc, maxc->next, CONF_PARAM,
          "MaxClientsPerClass", FALSE);
        continue;
      }

      maxclients = *((unsigned int *) maxc->argv[1]);
      break;
    }

    if (maxclients == 0) {
      maxc = find_config(main_server->conf, CONF_PARAM, "MaxClients", FALSE);

      if (maxc)
        maxclients = *((unsigned int *) maxc->argv[0]);
    }

    snprintf(mg_class_limit, sizeof(mg_class_limit), "%u", maxclients);

  } else {
    snprintf(mg_class_limit, sizeof(mg_class_limit), "%u",
      max_clients ? *max_clients : 0);
    snprintf(mg_cur_class, sizeof(mg_cur_class), "%u", 0);
  }

  snprintf(mg_max, sizeof(mg_max), "%u", max_clients ? *max_clients : 0);

  user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
  if (user == NULL)
    user = "";

  rfc1413_ident = pr_table_get(session.notes, "mod_ident.rfc1413-ident", NULL);
  if (rfc1413_ident == NULL) {
    rfc1413_ident = "UNKNOWN";
  }

  memset(buf, '\0', sizeof(buf));
  while (pr_fsio_gets(buf, sizeof(buf), fh) != NULL) {
    char *tmp;

    pr_signals_handle();

    buf[sizeof(buf)-1] = '\0';
    len = strlen(buf);

    while (len > 0 &&
           (buf[len-1] == '\r' || buf[len-1] == '\n')) {
      pr_signals_handle();

      buf[len-1] = '\0';
      len--;
    }

    /* Check for any Variable-type strings. */
    tmp = strstr(buf, "%{");
    while (tmp) {
      char *key, *tmp2;
      const char *val;

      pr_signals_handle();

      tmp2 = strchr(tmp, '}');
      if (tmp2 == NULL) {
        /* No closing '}' found in this string, so no need to look for any
         * aother '%{' opening sequence.  Just move on.
         */
        tmp = NULL;
        break;
      }

      key = pstrndup(p, tmp, tmp2 - tmp + 1);

      /* There are a couple of special-case keys to watch for:
       *
       *   env:$var
       *   time:$fmt
       *
       * The Var API does not easily support returning values for keys
       * where part of the value depends on part of the key.  That's why
       * these keys are handled here, instead of in pr_var_get().
       */

      if (strncmp(key, "%{time:", 7) == 0) {
        char time_str[128], *fmt;
        time_t now;
        struct tm *time_info;

        fmt = pstrndup(p, key + 7, strlen(key) - 8);

        now = time(NULL);
        time_info = pr_localtime(NULL, &now);

        memset(time_str, 0, sizeof(time_str));
        strftime(time_str, sizeof(time_str), fmt, time_info);

        val = pstrdup(p, time_str);

      } else if (strncmp(key, "%{env:", 6) == 0) {
        char *env_var;

        env_var = pstrndup(p, key + 6, strlen(key) - 7);
        val = pr_env_get(p, env_var);
        if (val == NULL) {
          pr_trace_msg("var", 4,
            "no value set for environment variable '%s', using \"(none)\"",
            env_var);
          val = "(none)";
        }

      } else {
        val = pr_var_get(key);
        if (val == NULL) {
          pr_trace_msg("var", 4,
            "no value set for name '%s', using \"(none)\"", key);
          val = "(none)";
        }
      }

      outs = sreplace(p, buf, key, val, NULL);
      sstrncpy(buf, outs, sizeof(buf));

      tmp = strstr(outs, "%{");
    }

    outs = sreplace(p, buf,
      "%C", (session.cwd[0] ? session.cwd : "(none)"),
      "%E", main_server->ServerAdmin,
      "%F", mg_size,
      "%f", mg_size_units,
      "%i", "0",
      "%K", "0",
      "%k", "0B",
      "%L", serverfqdn,
      "%M", mg_max,
      "%N", mg_cur,
      "%o", "0",
      "%R", (session.c && session.c->remote_name ?
        session.c->remote_name : "(unknown)"),
      "%T", mg_time,
      "%t", "0",
      "%U", user,
      "%u", rfc1413_ident,
      "%V", main_server->ServerName,
      "%x", session.conn_class ? session.conn_class->cls_name : "(unknown)",
      "%y", mg_cur_class,
      "%z", mg_class_limit,
      NULL);

    /* Always make sure that the lines we send are CRLF-terminated. */
    msg = pstrcat(p, msg, outs, "\r\n", NULL);

    /* Clear the buffer for the next read. */
    memset(buf, '\0', sizeof(buf));
  }

  return msg;
}
Beispiel #17
0
static void filetab_parse_table(wrap2_table_t *filetab) {
  unsigned int lineno = 0;
  char buf[MOD_WRAP2_FILE_BUFFER_SIZE] = {'\0'};

  while (pr_fsio_getline(buf, sizeof(buf), (pr_fh_t *) filetab->tab_handle,
      &lineno) != NULL) {
    char *ptr, *res = NULL, *service = NULL;
    size_t buflen = strlen(buf);

    if (buf[buflen-1] != '\n') {
      wrap2_log("file '%s': missing newline or line too long (%u) at line %u",
        filetab->tab_name, (unsigned int) buflen, lineno);
      continue;
    } 

    if (buf[0] == '#' || buf[strspn(buf, " \t\r\n")] == 0) {
      continue;
    }

    buf[buflen-1] = '\0';

    /* The list of daemons is from the start of the line to a ':' delimiter.
     * This list is assumed to be space-delimited; failure to match this
     * syntax will result in lack of desired results when doing the access
     * checks.
     */
    ptr = strchr(buf, ':');
    if (ptr == NULL) {
      wrap2_log("file '%s': badly formatted list of daemon/service names at "
        "line %u", filetab->tab_name, lineno);
      continue;
    }

    service = pstrndup(filetab->tab_pool, buf, (ptr - buf));

    if (filetab_service_name &&
        (strcasecmp(filetab_service_name, service) == 0 ||
         strncasecmp("ALL", service, 4) == 0)) {
      if (filetab_daemons_list == NULL) {
        filetab_daemons_list = make_array(filetab->tab_pool, 0, sizeof(char *));
      }

      *((char **) push_array(filetab_daemons_list)) = service;

      res = wrap2_strsplit(buf, ':');
      if (res == NULL) {
        wrap2_log("file '%s': missing \":\" separator at %u",
          filetab->tab_name, lineno);
        continue;
      }

      if (filetab_clients_list == NULL) {
        filetab_clients_list = make_array(filetab->tab_pool, 0, sizeof(char *));
      }

      /* Check for another ':' delimiter.  If present, anything following that
       * delimiter is an option/shell command (as per the hosts_access(5) man
       * page syntax description).
       *
       * If there are commas or whitespace in the line, parse them as separate
       * client names.  Otherwise, a comma- or space-delimited list of names
       * will be treated as a single name, and violate the principle of least
       * surprise for the site admin.
       *
       * NOTE: Disable support for options in the file syntax if IPv6 addresses
       * are present, since the parsing code below is not sufficient for
       * handling both IPv6 addresses AND options, e.g.:
       *
       *  proftpd: [::1] [::2]: <options>
       */

      ptr = strchr(res, ':');
      if (ptr != NULL) {
        char *clients;
        size_t clients_len;

        clients_len = (ptr - res);
        clients = pstrndup(filetab->tab_pool, res, clients_len);

        if (strcspn(clients, "[]") == clients_len) {
          ptr = wrap2_strsplit(res, ':');

          if (filetab_options_list == NULL) {
            filetab_options_list = make_array(filetab->tab_pool, 0, 
              sizeof(char *));
          }

          /* Skip redundant whitespaces */
          while (*ptr == ' ' ||
                 *ptr == '\t') {
            pr_signals_handle();
            ptr++;
          }

          *((char **) push_array(filetab_options_list)) =
            pstrdup(filetab->tab_pool, ptr);

        } else {
          /* Ignoring options and IPv6 addresses (Bug#4090) for now. */
        }

      } else {
        /* No options present. */
        ptr = res;
      }

      ptr = strpbrk(res, ", \t");
      if (ptr != NULL) {
        char *dup_opts, *word;

        dup_opts = pstrdup(filetab->tab_pool, res);
        while ((word = pr_str_get_token(&dup_opts, ", \t")) != NULL) {
          size_t wordlen;

          pr_signals_handle();

          wordlen = strlen(word);
          if (wordlen == 0) {
            continue;
          }

          /* Remove any trailing comma */
          if (word[wordlen-1] == ',') {
            word[wordlen-1] = '\0';
            wordlen--;
          }

          *((char **) push_array(filetab_clients_list)) = word;

          /* Skip redundant whitespaces */
          while (*dup_opts == ' ' ||
                 *dup_opts == '\t') {
            pr_signals_handle();
            dup_opts++;
          }
        }

      } else {
        *((char **) push_array(filetab_clients_list)) =
          pstrdup(filetab->tab_pool, res);
      }
 
    } else {
      wrap2_log("file '%s': skipping irrevelant daemon/service ('%s') line %u",
        filetab->tab_name, service, lineno);
    }
  }

  return;
}
Beispiel #18
0
const pr_netaddr_t *proxy_ftp_msg_parse_ext_addr(pool *p, const char *msg,
    const pr_netaddr_t *addr, int cmd_id, const char *net_proto) {
  pr_netaddr_t *res = NULL, na;
  int family = 0;
  unsigned short port = 0;
  char delim, *msg_str, *ptr;
  size_t msglen;

  if (p == NULL ||
      msg == NULL ||
      addr == NULL) {
    errno = EINVAL;
    return NULL;
  }

  if (cmd_id == PR_CMD_EPSV_ID) {
    /* First, find the opening '(' character. */
    ptr = strchr(msg, '(');
    if (ptr == NULL) {
      pr_trace_msg(trace_channel, 12,
        "missing starting '(' character for extended address in '%s'", msg);
      errno = EINVAL;
      return NULL;
    }

    /* Make sure that the last character is a closing ')'. */
    msglen = strlen(ptr);
    if (ptr[msglen-1] != ')') {
      pr_trace_msg(trace_channel, 12,
        "missing ending ')' character for extended address in '%s'", msg);
      errno = EINVAL;
      return NULL;
    }

    msg_str = pstrndup(p, ptr+1, msglen-2);

  } else {
    msg_str = pstrdup(p, msg);
  }

  /* Format is <d>proto<d>ip address<d>port<d> (ASCII in network order),
   * where <d> is an arbitrary delimiter character.
   */
  delim = *msg_str++;

  /* If the network protocol string (e.g. sent by client in EPSV command) is
   * null, then determine the protocol family from the address family we were
   * given.
   */

  /* XXX Hack to skip "all", e.g. "EPSV ALL" commands. */
  if (net_proto != NULL) {
    if (strncasecmp(net_proto, "all", 4) == 0) {
      net_proto = NULL;
    }
  }

  if (net_proto == NULL) {
    if (*msg_str == delim) {
      switch (pr_netaddr_get_family(addr)) {
        case AF_INET:
          family = 1;
          break;

#ifdef PR_USE_IPV6
        case AF_INET6:
          if (pr_netaddr_use_ipv6()) {
            family = 2;
            break;
          }
#endif /* PR_USE_IPV6 */

        default:
          break;
      }

    } else {
      family = atoi(msg_str);
    }

  } else {
    family = atoi(net_proto);
  }

  switch (family) {
    case 1:
      pr_trace_msg(trace_channel, 19, "parsed IPv4 address from '%s'", msg);
      break;

#ifdef PR_USE_IPV6
    case 2:
      pr_trace_msg(trace_channel, 19, "parsed IPv6 address from '%s'", msg);
      if (pr_netaddr_use_ipv6()) {
        break;
      }
#endif /* PR_USE_IPV6 */

    default:
      pr_trace_msg(trace_channel, 12,
        "unsupported network protocol %d", family);
      errno = EPROTOTYPE;
      return NULL;
  }

  /* Now, skip past those numeric characters that atoi() used. */
  while (PR_ISDIGIT(*msg_str)) {
    msg_str++;
  }

  /* If the next character is not the delimiter, it's a badly formatted
   * parameter.
   */
  if (*msg_str == delim) {
    msg_str++;

  } else {
    pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'",
      msg_str);
    errno = EPERM;
    return NULL;
  }

  pr_netaddr_clear(&na);

  /* If the next character IS the delimiter, then the address portion is
   * omitted (which is permissible).
   */
  if (*msg_str == delim) {
    pr_netaddr_set_family(&na, pr_netaddr_get_family(addr));
    pr_netaddr_set_sockaddr(&na, pr_netaddr_get_sockaddr(addr));
    msg_str++;

  } else {
    ptr = strchr(msg_str, delim);
    if (ptr == NULL) {
      /* Badly formatted message. */
      errno = EINVAL;
      return NULL;
    }

    /* Twiddle the string so that just the address portion will be processed
     * by pr_inet_pton().
     */
    *ptr = '\0';

    /* Use pr_inet_pton() to translate the address string into the address
     * value.
     */
    switch (family) {
      case 1: {
        struct sockaddr *sa = NULL;

        pr_netaddr_set_family(&na, AF_INET);
        sa = pr_netaddr_get_sockaddr(&na);
        if (sa) {
          sa->sa_family = AF_INET;
        }

        if (pr_inet_pton(AF_INET, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) {
          pr_trace_msg(trace_channel, 2,
            "error converting IPv4 address '%s': %s", msg_str, strerror(errno));
          errno = EPERM;
          return NULL;
        }

        break;
      }

      case 2: {
        struct sockaddr *sa = NULL;

        pr_netaddr_set_family(&na, AF_INET6);
        sa = pr_netaddr_get_sockaddr(&na);
        if (sa) {
          sa->sa_family = AF_INET6;
        }

        if (pr_inet_pton(AF_INET6, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) {
          pr_trace_msg(trace_channel, 2,
            "error converting IPv6 address '%s': %s", msg_str, strerror(errno));
          errno = EPERM;
          return NULL;
        }

        break;
      }
    }

    /* Advance past the address portion of the argument. */
    msg_str = ++ptr;
  }

  port = atoi(msg_str);

  while (PR_ISDIGIT(*msg_str)) {
    msg_str++;
  }

  /* If the next character is not the delimiter, it's a badly formatted
   * parameter.
   */
  if (*msg_str != delim) {
    pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'",
      msg_str);
    errno = EPERM;
    return NULL;
  }

  /* XXX Use a pool other than session.pool here, in the future. */ 
  res = pr_netaddr_dup(session.pool, &na);
  pr_netaddr_set_port(res, htons(port));

  return res;
}
Beispiel #19
0
/* Performs chroot-aware handling of symlinks. */
int dir_readlink(pool *p, const char *path, char *buf, size_t bufsz,
    int flags) {
  int is_abs_dst, clean_flags, len, res = -1;
  size_t chroot_pathlen = 0, adj_pathlen = 0;
  char *dst_path, *adj_path;
  pool *tmp_pool;

  if (p == NULL ||
      path == NULL ||
      buf == NULL) {
    errno = EINVAL;
    return -1;
  }

  if (bufsz == 0) {
    return 0;
  }

  len = pr_fsio_readlink(path, buf, bufsz);
  if (len < 0) {
    return -1;
  }

  if (len == 0 ||
      len == bufsz) {
    /* If we read nothing in, OR if the given buffer was completely
     * filled WITHOUT terminating NUL, there's really nothing we can/should
     * be doing.
     */
    return len;
  }

  is_abs_dst = FALSE;
  if (*buf == '/') {
    is_abs_dst = TRUE;
  }

  if (session.chroot_path != NULL) {
    chroot_pathlen = strlen(session.chroot_path);
  }

  if (chroot_pathlen <= 1) {
    char *ptr;

    if (is_abs_dst == TRUE ||
        !(flags & PR_DIR_READLINK_FL_HANDLE_REL_PATH)) {
      return len;
    }

    /* Since we have a relative destination path, we will concat it
     * with the source path's directory, then clean up that path.
     */
    ptr = strrchr(path, '/');
    if (ptr != NULL &&
        ptr != path) {
      char *parent_dir;

      tmp_pool = make_sub_pool(p);
      pr_pool_tag(tmp_pool, "dir_readlink pool");

      parent_dir = pstrndup(tmp_pool, path, (ptr - path));
      dst_path = pdircat(tmp_pool, parent_dir, buf, NULL);

      adj_pathlen = bufsz + 1;
      adj_path = pcalloc(tmp_pool, adj_pathlen);

      res = pr_fs_clean_path2(dst_path, adj_path, adj_pathlen-1, 0);
      if (res == 0) {
        pr_trace_msg("fsio", 19,
          "cleaned symlink path '%s', yielding '%s'", dst_path, adj_path);
        dst_path = adj_path;
      }

      pr_trace_msg("fsio", 19,
        "adjusted relative symlink path '%s', yielding '%s'", buf, dst_path);

      memset(buf, '\0', bufsz);
      sstrncpy(buf, dst_path, bufsz);
      len = strlen(buf);
      destroy_pool(tmp_pool);
    }

    return len;
  }

  if (is_abs_dst == FALSE) {
    /* If we are to ignore relative destination paths, return now. */
    if (!(flags & PR_DIR_READLINK_FL_HANDLE_REL_PATH)) {
      return len;
    }
  }

  if (is_abs_dst == TRUE &&
      len < chroot_pathlen) {
    /* If the destination path length is shorter than the chroot path,
     * AND the destination path is absolute, then by definition it CANNOT
     * point within the chroot.
     */
    return len;
  }

  tmp_pool = make_sub_pool(p);
  pr_pool_tag(tmp_pool, "dir_readlink pool");

  dst_path = pstrdup(tmp_pool, buf);
  if (is_abs_dst == FALSE) {
    char *ptr;

    /* Since we have a relative destination path, we will concat it
     * with the source path's directory, then clean up that path.
     */

    ptr = strrchr(path, '/');
    if (ptr != NULL &&
        ptr != path) {
      char *parent_dir;

      parent_dir = pstrndup(tmp_pool, path, (ptr - path));
      dst_path = pdircat(tmp_pool, parent_dir, dst_path, NULL);

    } else {
      dst_path = pdircat(tmp_pool, path, dst_path, NULL);
    }
  }

  adj_pathlen = bufsz + 1;
  adj_path = pcalloc(tmp_pool, adj_pathlen);

  clean_flags = PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH;
  res = pr_fs_clean_path2(dst_path, adj_path, adj_pathlen-1, clean_flags);
  if (res == 0) {
    pr_trace_msg("fsio", 19,
      "cleaned symlink path '%s', yielding '%s'", dst_path, adj_path);
    dst_path = adj_path;

    memset(buf, '\0', bufsz);
    sstrncpy(buf, dst_path, bufsz);
    len = strlen(dst_path);
  }

  if (strncmp(dst_path, session.chroot_path, chroot_pathlen) == 0 &&
      *(dst_path + chroot_pathlen) == '/') {
    char *ptr;

    ptr = dst_path + chroot_pathlen;

    if (is_abs_dst == FALSE &&
        res == 0) {
      /* If we originally had a relative destination path, AND we cleaned
       * that adjusted path, then we should try to re-adjust the path
       * back to being a relative path.  Within reason.
       */
      ptr = pstrcat(tmp_pool, ".", ptr, NULL);
    }

    /* Since we are making the destination path shorter, the given buffer
     * (which was big enough for the original destination path) should
     * always be large enough for this adjusted, shorter version.  Right?
     */
    pr_trace_msg("fsio", 19,
      "adjusted symlink path '%s' for chroot '%s', yielding '%s'",
      dst_path, session.chroot_path, ptr);

    memset(buf, '\0', bufsz);
    sstrncpy(buf, ptr, bufsz);
    len = strlen(buf);
  }

  destroy_pool(tmp_pool);
  return len;
}