|  | @@ -73,6 +73,10 @@ static ssize_t rend_service_parse_intro_for_v3(
 | 
	
		
			
				|  |  |      size_t plaintext_len,
 | 
	
		
			
				|  |  |      char **err_msg_out);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static int rend_service_check_private_dir(const or_options_t *options,
 | 
	
		
			
				|  |  | +                                          const rend_service_t *s,
 | 
	
		
			
				|  |  | +                                          int create);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /** Represents the mapping from a virtual port of a rendezvous service to
 | 
	
		
			
				|  |  |   * a real port on some IP.
 | 
	
		
			
				|  |  |   */
 | 
	
	
		
			
				|  | @@ -472,6 +476,11 @@ rend_config_services(const or_options_t *options, int validate_only)
 | 
	
		
			
				|  |  |    for (line = options->RendConfigLines; line; line = line->next) {
 | 
	
		
			
				|  |  |      if (!strcasecmp(line->key, "HiddenServiceDir")) {
 | 
	
		
			
				|  |  |        if (service) { /* register the one we just finished parsing */
 | 
	
		
			
				|  |  | +        if (rend_service_check_private_dir(options, service, 0) < 0) {
 | 
	
		
			
				|  |  | +          rend_service_free(service);
 | 
	
		
			
				|  |  | +          return -1;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          if (validate_only)
 | 
	
		
			
				|  |  |            rend_service_free(service);
 | 
	
		
			
				|  |  |          else
 | 
	
	
		
			
				|  | @@ -687,12 +696,7 @@ rend_config_services(const or_options_t *options, int validate_only)
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (service) {
 | 
	
		
			
				|  |  | -    cpd_check_t check_opts = CPD_CHECK_MODE_ONLY|CPD_CHECK;
 | 
	
		
			
				|  |  | -    if (service->dir_group_readable) {
 | 
	
		
			
				|  |  | -      check_opts |= CPD_GROUP_READ;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if (check_private_dir(service->directory, check_opts, options->User) < 0) {
 | 
	
		
			
				|  |  | +    if (rend_service_check_private_dir(options, service, 0) < 0) {
 | 
	
		
			
				|  |  |        rend_service_free(service);
 | 
	
		
			
				|  |  |        return -1;
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -1016,7 +1020,9 @@ service_is_single_onion_poisoned(const rend_service_t *service)
 | 
	
		
			
				|  |  |    fstatus = file_status(poison_fname);
 | 
	
		
			
				|  |  |    tor_free(poison_fname);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /* If this fname is occupied, the hidden service has been poisoned. */
 | 
	
		
			
				|  |  | +  /* If this fname is occupied, the hidden service has been poisoned.
 | 
	
		
			
				|  |  | +   * fstatus can be FN_ERROR if the service directory does not exist, in that
 | 
	
		
			
				|  |  | +   * case, there is obviously no private key. */
 | 
	
		
			
				|  |  |    if (fstatus == FN_FILE || fstatus == FN_EMPTY) {
 | 
	
		
			
				|  |  |      return 1;
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -1032,7 +1038,9 @@ rend_service_private_key_exists(const rend_service_t *service)
 | 
	
		
			
				|  |  |    char *private_key_path = rend_service_path(service, private_key_fname);
 | 
	
		
			
				|  |  |    const file_status_t private_key_status = file_status(private_key_path);
 | 
	
		
			
				|  |  |    tor_free(private_key_path);
 | 
	
		
			
				|  |  | -  /* Only non-empty regular private key files could have been used before. */
 | 
	
		
			
				|  |  | +  /* Only non-empty regular private key files could have been used before.
 | 
	
		
			
				|  |  | +   * fstatus can be FN_ERROR if the service directory does not exist, in that
 | 
	
		
			
				|  |  | +   * case, there is obviously no private key. */
 | 
	
		
			
				|  |  |    return private_key_status == FN_FILE;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1105,6 +1113,10 @@ poison_new_single_onion_hidden_service_dir(const rend_service_t *service)
 | 
	
		
			
				|  |  |      return -1;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  /* Make sure the directory exists */
 | 
	
		
			
				|  |  | +  if (rend_service_check_private_dir(get_options(), service, 1) < 0)
 | 
	
		
			
				|  |  | +    return -1;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    poison_fname = rend_service_sos_poison_path(service);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    switch (file_status(poison_fname)) {
 | 
	
	
		
			
				|  | @@ -1260,6 +1272,37 @@ rend_service_derive_key_digests(struct rend_service_t *s)
 | 
	
		
			
				|  |  |    return 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/** Make sure that the directory for <b>s</b> is private, using the config in
 | 
	
		
			
				|  |  | + * <b>options</b>.
 | 
	
		
			
				|  |  | + * If <b>create</b> is true:
 | 
	
		
			
				|  |  | + *  - if the directory exists, change permissions if needed,
 | 
	
		
			
				|  |  | + *  - if the directory does not exist, create it with the correct permissions.
 | 
	
		
			
				|  |  | + * If <b>create</b> is false:
 | 
	
		
			
				|  |  | + *  - if the directory exists, check permissions,
 | 
	
		
			
				|  |  | + *  - if the directory does not exist, check if we think we can create it.
 | 
	
		
			
				|  |  | + * Return 0 on success, -1 on failure. */
 | 
	
		
			
				|  |  | +static int
 | 
	
		
			
				|  |  | +rend_service_check_private_dir(const or_options_t *options,
 | 
	
		
			
				|  |  | +                               const rend_service_t *s,
 | 
	
		
			
				|  |  | +                               int create)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  cpd_check_t  check_opts = CPD_NONE;
 | 
	
		
			
				|  |  | +  if (create) {
 | 
	
		
			
				|  |  | +    check_opts |= CPD_CREATE;
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    check_opts |= CPD_CHECK_MODE_ONLY;
 | 
	
		
			
				|  |  | +    check_opts |= CPD_CHECK;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (s->dir_group_readable) {
 | 
	
		
			
				|  |  | +    check_opts |= CPD_GROUP_READ;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  /* Check/create directory */
 | 
	
		
			
				|  |  | +  if (check_private_dir(s->directory, check_opts, options->User) < 0) {
 | 
	
		
			
				|  |  | +    return -1;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /** Load and/or generate private keys for the hidden service <b>s</b>,
 | 
	
		
			
				|  |  |   * possibly including keys for client authorization.  Return 0 on success, -1
 | 
	
		
			
				|  |  |   * on failure. */
 | 
	
	
		
			
				|  | @@ -1268,24 +1311,9 @@ rend_service_load_keys(rend_service_t *s)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    char *fname = NULL;
 | 
	
		
			
				|  |  |    char buf[128];
 | 
	
		
			
				|  |  | -  cpd_check_t  check_opts = CPD_CREATE;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  if (s->dir_group_readable) {
 | 
	
		
			
				|  |  | -    check_opts |= CPD_GROUP_READ;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  /* Check/create directory */
 | 
	
		
			
				|  |  | -  if (check_private_dir(s->directory, check_opts, get_options()->User) < 0) {
 | 
	
		
			
				|  |  | +  if (rend_service_check_private_dir(get_options(), s, 1) < 0)
 | 
	
		
			
				|  |  |      goto err;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -#ifndef _WIN32
 | 
	
		
			
				|  |  | -  if (s->dir_group_readable) {
 | 
	
		
			
				|  |  | -    /* Only new dirs created get new opts, also enforce group read. */
 | 
	
		
			
				|  |  | -    if (chmod(s->directory, 0750)) {
 | 
	
		
			
				|  |  | -      log_warn(LD_FS,"Unable to make %s group-readable.",
 | 
	
		
			
				|  |  | -      rend_service_escaped_dir(s));
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* Load key */
 | 
	
		
			
				|  |  |    fname = rend_service_path(s, private_key_fname);
 |