Files @ c318109520cc
Branch filter:

Location: DistRen/src/common/request.c

binki
User authentication and some access checking.
/*
 * Copyright 2010 Nathan Phillip Brink
 *
 * This file is a part of DistRen.
 *
 * DistRen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DistRen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with DistRen.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "common/config.h"

#include "common/request.h"

#include "common/protocol.h"

#include <arpa/inet.h>
#include <openssl/evp.h>
#include <stdlib.h>
#include <string.h>

int distren_request_free_with_data(struct distren_request *req, void *data)
{
  free(data);
  return distren_request_free(req);
}

int distren_request_version(struct distren_request **req, void **data, uint32_t servertype, const char *package_string)
{
  struct distren_request_version *version;

  distren_request_new(req, sizeof(struct distren_request_version), DISTREN_REQUEST_VERSION);
  version = malloc(sizeof(struct distren_request_version));
  if(!version || !*req)
    {
      free(version);
      if(*req)
	distren_request_free(*req);
      return 1;
    }

  memset(version, 0, sizeof(struct distren_request_version));
  version->servertype = servertype;
  strncpy(version->package_string, package_string, DISTREN_REQUEST_VERSION_PACKAGE_STRING_LEN);

  *data = version;

  return 0;
}

int distren_request_parse_version(struct distren_request *req, void *data, struct distren_request_version *version)
{
  if(req->len < sizeof(struct distren_request_version))
    return 1;

  memcpy(version, data, sizeof(struct distren_request_version));
  /* there is space for another '\0' */
  version->package_string[DISTREN_REQUEST_VERSION_PACKAGE_STRING_LEN] = '\0';

  return 0;
}

int distren_request_pass(struct distren_request **req, void **data, const char *username, const char *pass)
{
  struct distren_request_pass *request_pass;

  distren_request_new(req, sizeof(struct distren_request_pass), DISTREN_REQUEST_PASS);
  request_pass = malloc(sizeof(struct distren_request_pass));
  if(!request_pass || !*req)
    {
      if(*req)
	distren_request_free(*req);
      free(request_pass);

      return 1;
    }

  /*
   * The packet itself doesn't need the string's NULL terminator
   * _unless_ if the string is shorter than the maximum length of the
   * username or password. Thus, strncpy()'s behavior (not strlcpy()'s
   * behavior) is _exactly_ what we want for this case.
   */
  strncpy(request_pass->username, username, DISTREN_REQUEST_PASS_USERNAME_LEN);
  strncpy(request_pass->pass, pass, DISTREN_REQUEST_PASS_PASS_LEN);

  *data = request_pass;

  return 0;
}

int distren_request_poing(struct distren_request **req, void **data, short is_ping, const void *poing_cookie, size_t poing_data_len)
{
  enum distren_request_type type;

  if(is_ping)
    type = DISTREN_REQUEST_PING;
  else
    type = DISTREN_REQUEST_PONG;
  distren_request_new(req, poing_data_len, type);
  (*data) = malloc(poing_data_len);
  memcpy(*data, poing_cookie, poing_data_len);

  return 0;
}

/*
 * file posting stuffs
 */
struct distren_request_file_post_context
{
  EVP_MD_CTX *digest_ctx;
  /* stored in network-byte order */
  uint32_t post_id;
};

/**
 * Helper for distren_request_file_post_start() and
 * distren_request_parse_file_post_start().
 */
static int _distren_request_file_post_context_new(distren_request_file_post_context_t *post_context,
						  uint32_t post_id)
{
  *post_context = malloc(sizeof(struct distren_request_file_post_context));
  if(!*post_context)
    return 1;

  (*post_context)->post_id = htonl(post_id);
  (*post_context)->digest_ctx = EVP_MD_CTX_create();
  EVP_DigestInit_ex((*post_context)->digest_ctx, EVP_sha1(), NULL);

  return 0;
}

/**
 * Frees a post_context and sets the pointer to NULL.
 */
static int _distren_request_file_post_context_free(distren_request_file_post_context_t *post_context)
{
  if(!*post_context)
    return 0;

  EVP_MD_CTX_destroy((*post_context)->digest_ctx);
  free(*post_context);

  *post_context = NULL;

  return 0;
}

int distren_request_file_post_context_free(distren_request_file_post_context_t post_context)
{
  /*
   * for the ``public'' function, we don't need to have post_context
   * set to NULL. Thus, we have this wrapper around
   * _distren_request_file_post_context_free().
   */
  return _distren_request_file_post_context_free(&post_context);
}

int distren_request_file_post_start(struct distren_request **req,
				    void **data,
				    distren_request_file_post_context_t *post_context,
				    uint32_t post_id,
				    const char *filename)
{
  struct distren_request_file_post_start *file_post_start;

  distren_request_new(req, sizeof(struct distren_request_file_post_start), DISTREN_REQUEST_FILE_POST_START);
  file_post_start = malloc(sizeof(struct distren_request_file_post_start));
  _distren_request_file_post_context_new(post_context, post_id);
  if(!*req
     || !file_post_start)
    {
      if(*req)
	distren_request_free(*req);
      free(file_post_start);
      _distren_request_file_post_context_free(post_context);
      return 1;
    }

  file_post_start->post_id = (*post_context)->post_id;
  strncpy(file_post_start->filename, filename, DISTREN_REQUEST_FILE_POST_NAME_LEN);

  *data = file_post_start;
  return 0;
}

int distren_request_parse_file_post_start(struct distren_request *req,
					  void *data,
					  distren_request_file_post_context_t *post_context,
					  uint32_t *post_id,
					  char **filename)
{
  int tmp;
  struct distren_request_file_post_start *file_post_start;

  /* keep our promises ;-) */
  *post_context = NULL;
  *filename = NULL;

  if(req->len < sizeof(struct distren_request_file_post_start))
    return 1;

  file_post_start = data;

  tmp = _distren_request_file_post_context_new(post_context, file_post_start->post_id);
  *filename = malloc(DISTREN_REQUEST_FILE_POST_NAME_LEN + 1);
  if(tmp
     || !*filename)
    {
      _distren_request_file_post_context_free(post_context);
      free(*filename);
    }

  memcpy(*filename, file_post_start->filename, DISTREN_REQUEST_FILE_POST_NAME_LEN);
  (*filename)[DISTREN_REQUEST_FILE_POST_NAME_LEN] = '\0';

  *post_id = ntohl(file_post_start->post_id);

  return 0;
}

int distren_request_file_post(struct distren_request **req,
			      struct distren_request_file_post *data_header,
			      distren_request_file_post_context_t post_context,
			      const void *data,
			      size_t len)
{
  distren_request_new(req, sizeof(struct distren_request_file_post) + len, DISTREN_REQUEST_FILE_POST);
  if(!*req)
    return 1;

  data_header->post_id = post_context->post_id;

  EVP_DigestUpdate(post_context->digest_ctx, data, len);

  return 0;
}

int distren_request_parse_file_post(struct distren_request *req,
				    void *data,
				    uint32_t *post_id,
				    distren_request_parse_file_post_find_context_func_t find_context_func,
				    void *find_post_context_data,
				    void **file_data,
				    size_t *file_data_len)
{
  struct distren_request_file_post *file_post;
  distren_request_file_post_context_t post_context;

  if(req->type != DISTREN_REQUEST_FILE_POST
     || req->len < sizeof(struct distren_request_file_post))
    return 1;

  file_post = data;
  *post_id = ntohl(file_post->post_id);
  post_context = (*find_context_func)(*post_id, find_post_context_data);
  if(!post_context
     || *post_id != post_context->post_id)
    return 1;

  *file_data = data + sizeof(struct distren_request_file_post);
  *file_data_len = req->len - sizeof(struct distren_request_file_post);
  EVP_DigestUpdate(post_context->digest_ctx, *file_data, *file_data_len);

  return 0;
}

int distren_request_file_post_finish(struct distren_request **req,
				     void **data,
				     distren_request_file_post_context_t post_context,
				     uint32_t cancel)
{
  struct distren_request_file_post_finish *file_post_finish;

  distren_request_new(req, sizeof(struct distren_request_file_post_finish), DISTREN_REQUEST_FILE_POST_FINISH);
  file_post_finish = malloc(sizeof(struct distren_request_file_post_finish));
  if(!*req
     || !file_post_finish)
    {
      if(*req)
	distren_request_free(*req);
      free(file_post_finish);

      _distren_request_file_post_context_free(&post_context);
      return 1;
    }
  file_post_finish->post_id = post_context->post_id;
  file_post_finish->cancel = htonl(cancel);

  EVP_DigestFinal(post_context->digest_ctx, file_post_finish->sha, NULL);
  _distren_request_file_post_context_free(&post_context);

  *data = file_post_finish;
  return 0;
}

int distren_request_parse_file_post_finish(struct distren_request *req,
				    void *data,
				    uint32_t *post_id,
				    distren_request_parse_file_post_find_context_func_t find_context_func,
				    void *find_post_context_data)
{
  struct distren_request_file_post_finish *file_post_finish;
  distren_request_file_post_context_t post_context;

  unsigned int checksum_len;
  unsigned char checksum[EVP_MAX_MD_SIZE];

  if(req->type != DISTREN_REQUEST_FILE_POST_FINISH
     || req->len < sizeof(struct distren_request_file_post_finish))
    return 1;

  file_post_finish = data;
  *post_id = ntohl(file_post_finish->post_id);
  if(ntohl(file_post_finish->cancel))
    return 2;

  post_context = find_context_func(*post_id, find_post_context_data);
  if(!post_context)
    return 1;

  /* finish up checksumming */
  EVP_DigestFinal_ex(post_context->digest_ctx, checksum, &checksum_len);
  if(checksum_len > EVP_MAX_MD_SIZE)
    checksum_len = EVP_MAX_MD_SIZE;
  if(memcmp(checksum, file_post_finish->sha, checksum_len))
    return 3;

  return 0;
}