Exemple #1
0
void
rcv_account_read (struct htlc_conn *htlc)
{
   struct hl_access_bits acc;
   u_int8_t login[32], name[32];
   u_int16_t llen, nlen;
   int err;

   dh_start(htlc)
      if (dh_type != HTLC_DATA_LOGIN)
         continue;
      llen = dh_len > 31 ? 31 : dh_len;
      memcpy(login, dh_data, llen);
      login[llen] = 0;
      if ((err = account_read(login, 0, name, &acc))) {
         snd_strerror(htlc, err);
         continue;
      }
      
      hl_encode(login, login, llen);
      nlen = strlen(name);
      hlwrite(htlc, HTLS_HDR_TASK, 0, 4,
         HTLS_DATA_NAME, nlen, name,
         HTLS_DATA_LOGIN, llen, login,
         HTLS_DATA_PASSWORD, 1, "\0",
         HTLS_DATA_ACCESS, 8, &acc);
   dh_end()
}
Exemple #2
0
void
hx_connect (struct htlc_conn *htlc, const char *serverstr, u_int16_t port,
	    const char *name, u_int16_t icon, const char *login, const char *pass,
	    int secure)
{
	int s;
	struct SOCKADDR_IN saddr;
	char abuf[HOSTLEN+1];

#if !defined(__WIN32__)
	if (serverstr[0] == '|' && serverstr[1]) {
		int pfds[2];
		int r;

		if (htlc->fd)
			hx_htlc_close(htlc);
		r = fd_popen(pfds, serverstr+1);
		if (r < 0) {
			hx_printf_prefix(htlc, 0, INFOPREFIX, "fd_popen(%s): %s\n",
					serverstr+1, strerror(errno));
			return;
		}
		hx_printf_prefix(htlc, 0, INFOPREFIX, "connecting through pipe %s\n", serverstr+1);
		htlc->fd = pfds[0];
		htlc->wfd = pfds[1];
		s = htlc->fd;
		fd_blocking(s, 0);
		fd_closeonexec(s, 1);
		hxd_files[s].ready_write = 0;
		hxd_files[s].ready_read = htlc_read;
		hxd_files[s].conn.htlc = htlc;
		hxd_fd_add(s);
		hxd_fd_set(s, FDR);
		if (high_fd < s)
			high_fd = s;
		s = htlc->wfd;
		fd_blocking(s, 0);
		fd_closeonexec(s, 1);
		hxd_files[s].ready_read = 0;
		hxd_files[s].ready_write = htlc_write;
		hxd_files[s].conn.htlc = htlc;
		hxd_fd_add(s);
		hxd_fd_set(s, FDW);
		if (high_fd < s)
			high_fd = s;
		goto is_pipe;
	}
#endif
#if defined(CONFIG_UNIXSOCKETS)
	else if (serverstr[0] == '!' && serverstr[1]) {
		struct sockaddr_un usaddr;

		s = socket(AF_UNIX, SOCK_STREAM, 0);
		if (s < 0) {
			hx_printf_prefix(htlc, 0, INFOPREFIX, "socket: %s\n", strerror(errno));
			return;
		}
		usaddr.sun_family = AF_UNIX;
		snprintf(usaddr.sun_path, sizeof(usaddr.sun_path), "%s", serverstr+1);

		if (htlc->fd)
			hx_htlc_close(htlc);
		if (connect(s, (struct sockaddr *)&usaddr, sizeof(usaddr))) {
			switch (errno) {
				case EINPROGRESS:
					break;
				default:
					hx_printf_prefix(htlc, 0, INFOPREFIX, "connect: %s\n", strerror(errno));
					socket_close(s);
					return;
			}
		}
		htlc->usockaddr = usaddr;
		socket_blocking(s, 0);
		fd_closeonexec(s, 1);
		goto is_unix;
	}
#endif

	s = socket(AFINET, SOCK_STREAM, IPPROTO_TCP);
	if (s < 0) {
		hx_printf_prefix(htlc, 0, INFOPREFIX, "socket: %s\n", strerror(errno));
		return;
	}
	if (s >= hxd_open_max) {
		hx_printf_prefix(htlc, 0, INFOPREFIX, "%s:%d: %d >= hxd_open_max (%d)", __FILE__, __LINE__, s, hxd_open_max);
		close(s);
		return;
	}
	socket_blocking(s, 0);
	fd_closeonexec(s, 1);

	if (hx_hostname[0]) {
#ifdef CONFIG_IPV6
		if (!inet_pton(AFINET, hx_hostname, &saddr.SIN_ADDR)) {
#else
		if (!inet_aton(hx_hostname, &saddr.SIN_ADDR)) {
#endif
			struct hostent *he;

			if ((he = gethostbyname(hx_hostname))) {
				size_t len = (unsigned int)he->h_length > sizeof(struct IN_ADDR)
					     ? sizeof(struct IN_ADDR) : (unsigned int)he->h_length;
				memcpy(&saddr.SIN_ADDR, he->h_addr, len);
			} else {
#ifndef HAVE_HSTRERROR
				hx_printf_prefix(htlc, 0, INFOPREFIX, "DNS lookup for %s failed\n", hx_hostname);
#else
				hx_printf_prefix(htlc, 0, INFOPREFIX, "DNS lookup for %s failed: %s\n", hx_hostname, hstrerror(h_errno));
#endif
				socket_close(s);
				return;
			}
		}
		saddr.SIN_PORT = 0;
		saddr.SIN_FAMILY = AFINET;
		if (bind(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
			inaddr2str(abuf, &saddr);
			hx_printf_prefix(htlc, 0, INFOPREFIX, "bind %s (%s): %s\n", hx_hostname, abuf, strerror(errno));
			socket_close(s);
			return;
		}
	}

#ifdef CONFIG_IPV6
	if (!inet_pton(AFINET, serverstr, &saddr.SIN_ADDR)) {
#else
	if (!inet_aton(serverstr, &saddr.SIN_ADDR)) {
#endif
		struct hostent *he;

		if ((he = gethostbyname(serverstr))) {
			size_t len = (unsigned int)he->h_length > sizeof(struct IN_ADDR)
				     ? sizeof(struct IN_ADDR) : (unsigned int)he->h_length;
			memcpy(&saddr.SIN_ADDR, he->h_addr, len);
		} else {
#ifndef HAVE_HSTRERROR
			hx_printf_prefix(htlc, 0, INFOPREFIX, "DNS lookup for %s failed\n", serverstr);
#else
			hx_printf_prefix(htlc, 0, INFOPREFIX, "DNS lookup for %s failed: %s\n", serverstr, hstrerror(h_errno));
#endif
			socket_close(s);
			return;
		}
	}
	saddr.SIN_PORT = htons(port);
	saddr.SIN_FAMILY = AFINET;

	if (htlc->fd)
		hx_htlc_close(htlc);
	if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr))) {
#if !defined(__WIN32__)
		switch (errno) {
			case EINPROGRESS:
				break;
			default:
				hx_printf_prefix(htlc, 0, INFOPREFIX, "connect: %s\n", strerror(errno));
				socket_close(s);
				return;
		}
#else
		int wsaerr;
		wsaerr = WSAGetLastError();
		if (wsaerr != WSAEWOULDBLOCK) {
			hx_printf_prefix(htlc, 0, INFOPREFIX, "connect: WSA error %d\n", wsaerr);
			socket_close(s);
			return;
		}
#endif
	}
	htlc->sockaddr = saddr;
	inaddr2str(abuf, &saddr);
	hx_printf_prefix(htlc, 0, INFOPREFIX, "connecting to %s:%u\n", abuf, ntohs(saddr.SIN_PORT));

is_unix:
	hxd_files[s].ready_read = htlc_read;
	hxd_files[s].ready_write = htlc_write_connect;
	hxd_files[s].conn.htlc = htlc;
	hxd_fd_add(s);
	hxd_fd_set(s, FDR|FDW);
	htlc->fd = s;

is_pipe:
	hx_output.status();

	if (name)
		strlcpy(htlc->name, name, sizeof(htlc->name));
	if (icon)
		htlc->icon = icon;
	if (login)
		strlcpy(htlc->login, login, sizeof(htlc->login));
	if (pass)
		strlcpy(htlc->password, pass, sizeof(htlc->password));
	else
		htlc->password[0] = 0;
	htlc->secure = secure;
	htlc->flags.in_login = 1;
	htlc->rcv = hx_rcv_magic;
	htlc->trans = 1;
	memset(&htlc->in, 0, sizeof(struct qbuf));
	memset(&htlc->out, 0, sizeof(struct qbuf));
	qbuf_set(&htlc->in, 0, HTLS_MAGIC_LEN);
	qbuf_add(&htlc->out, HTLC_MAGIC, HTLC_MAGIC_LEN);
}

void
hx_connect_finish (struct htlc_conn *htlc)
{
	char enclogin[64], encpass[64];
	char abuf[HOSTLEN+1];
	u_int16_t icon16;
	u_int16_t llen, plen;
	int secure;
	u_int8_t *login, *pass;

	secure = htlc->secure;
	login = htlc->login;
	pass = htlc->password;
	inaddr2str(abuf, &htlc->sockaddr);
#ifdef CONFIG_HOPE
	if (secure) {
#ifdef CONFIG_CIPHER
		u_int8_t cipheralglist[64];
		u_int16_t cipheralglistlen;
		u_int8_t cipherlen;
#endif
#ifdef CONFIG_COMPRESS
		u_int8_t compressalglist[64];
		u_int16_t compressalglistlen;
		u_int8_t compresslen;
#endif
		u_int16_t hc;
		u_int8_t macalglist[64];
		u_int16_t macalglistlen;

		abuf[0] = 0;
		task_new(htlc, rcv_task_login, htlc, 0, "login");
		strcpy(htlc->macalg, "HMAC-SHA1");
		S16HTON(2, macalglist);
		macalglistlen = 2;
		macalglist[macalglistlen] = 9;
		macalglistlen++;
		memcpy(macalglist+macalglistlen, htlc->macalg, 9);
		macalglistlen += 9;
		macalglist[macalglistlen] = 8;
		macalglistlen++;
		memcpy(macalglist+macalglistlen, "HMAC-MD5", 8);
		macalglistlen += 8;

		hc = 4;
#ifdef CONFIG_COMPRESS
		if (htlc->compressalg[0]) {
			compresslen = strlen(htlc->compressalg);
			S16HTON(1, compressalglist);
			compressalglistlen = 2;
			compressalglist[compressalglistlen] = compresslen;
			compressalglistlen++;
			memcpy(compressalglist+compressalglistlen, htlc->compressalg, compresslen);
			compressalglistlen += compresslen;
			hc++;
		} else
			compressalglistlen = 0;
#endif
#ifdef CONFIG_CIPHER
		if (htlc->cipheralg[0]) {
			cipherlen = strlen(htlc->cipheralg);
			S16HTON(1, cipheralglist);
			cipheralglistlen = 2;
			cipheralglist[cipheralglistlen] = cipherlen;
			cipheralglistlen++;
			memcpy(cipheralglist+cipheralglistlen, htlc->cipheralg, cipherlen);
			cipheralglistlen += cipherlen;
			hc++;
		} else
			cipheralglistlen = 0;
#endif
		hlwrite(htlc, HTLC_HDR_LOGIN, 0, hc,
			HTLC_DATA_LOGIN, 1, abuf,
			HTLC_DATA_PASSWORD, 1, abuf,
			HTLC_DATA_MAC_ALG, macalglistlen, macalglist,
#ifdef CONFIG_CIPHER
			HTLC_DATA_CIPHER_ALG, cipheralglistlen, cipheralglist,
#endif
#ifdef CONFIG_COMPRESS
			HTLC_DATA_COMPRESS_ALG, compressalglistlen, compressalglist,
#endif
			HTLC_DATA_SESSIONKEY, 0, 0);
		return;
	}
#endif /* HOPE */

	task_new(htlc, rcv_task_login, 0, 0, "login");

	icon16 = htons(htlc->icon);
	if (login) {
		llen = strlen(login);
		if (llen > 32)
			llen = 32;
		hl_encode(enclogin, login, llen);
	} else
		llen = 0;
	htlc->clientversion = g_clientversion;
	if (htlc->clientversion >= 150) {
		if (pass) {
			u_int16_t cv = htons(185);
			plen = strlen(pass);
			if (plen > 32)
				plen = 32;
			hl_encode(encpass, pass, plen);
			hlwrite(htlc, HTLC_HDR_LOGIN, 0, 3,
				HTLC_DATA_LOGIN, llen, enclogin,
				HTLC_DATA_PASSWORD, plen, encpass,
				HTLC_DATA_CLIENTVERSION, 2, &cv);
		} else {
			u_int16_t cv = htons(185);
			hlwrite(htlc, HTLC_HDR_LOGIN, 0, 2,
				HTLC_DATA_LOGIN, llen, enclogin,
				HTLC_DATA_CLIENTVERSION, 2, &cv);
		}
	} else { /* 123 */
		if (pass) {
			plen = strlen(pass);
			if (plen > 32)
				plen = 32;
			hl_encode(encpass, pass, plen);
			hlwrite(htlc, HTLC_HDR_LOGIN, 0, 4,
				HTLC_DATA_ICON, 2, &icon16,
				HTLC_DATA_LOGIN, llen, enclogin,
				HTLC_DATA_PASSWORD, plen, encpass,
				HTLC_DATA_NAME, strlen(htlc->name), htlc->name);
		} else {
			hlwrite(htlc, HTLC_HDR_LOGIN, 0, 3,
				HTLC_DATA_ICON, 2, &icon16,
				HTLC_DATA_LOGIN, llen, enclogin,
				HTLC_DATA_NAME, strlen(htlc->name), htlc->name);
		}
	}
	memset(htlc->password, 0, sizeof(htlc->password));
}
Exemple #3
0
static void
account_getdirs (const char *login, char *rootdir, char *newsfile, char *dropbox)
{
   struct stat sb;
   char dir[MAXPATHLEN];

   if (snprintf(dir, MAXPATHLEN, "%s/%s/files", hxd_cfg.paths.accounts, login) == -1) {
      hxd_log("account_getdirs(%s): path '%s' too long?!?", login, dir);
      goto default_files;
   }

#ifdef HAVE_CORESERVICES
   if (!resolve_alias_path(dir, rootdir) || stat(rootdir, &sb)) {
#else
   if (!realpath(dir, rootdir) || stat(rootdir, &sb)) {
#endif
default_files:
#ifdef HAVE_CORESERVICES
      if (!resolve_alias_path(hxd_cfg.paths.files, rootdir)) {
#else
      if (!realpath(hxd_cfg.paths.files, rootdir)) {
#endif
         hxd_log("could not get realpath of files dir '%s'", hxd_cfg.paths.files);
         snprintf(rootdir, MAXPATHLEN, "%s", hxd_cfg.paths.files);
      }
   }

   if (snprintf(dir, MAXPATHLEN, "%s/%s/news", hxd_cfg.paths.accounts, login) == -1) {
      hxd_log("account_getdirs(%s): path '%s' too long?!?", login, dir);
      goto default_news;
   }

#ifdef HAVE_CORESERVICES
   if (!resolve_alias_path(dir, newsfile) || stat(newsfile, &sb)) {
#else
   if (!realpath(dir, newsfile) || stat(newsfile, &sb)) {
#endif
default_news:
#ifdef HAVE_CORESERVICES
      if (!resolve_alias_path(hxd_cfg.paths.news, newsfile)) {
#else
      if (!realpath(hxd_cfg.paths.news, newsfile)) {
#endif
         hxd_log("could not get realpath of news file '%s'", hxd_cfg.paths.news);
         snprintf(newsfile, MAXPATHLEN, "%s", hxd_cfg.paths.news);
      }
   }

   if (snprintf(dir, MAXPATHLEN, "%s/%s/dropbox", hxd_cfg.paths.accounts, login) == -1) {
      hxd_log("account_getdirs(%s): path '%s' too long?!?", login, dir);
      goto default_dropbox;
   }

#ifdef HAVE_CORESERVICES
   if (!resolve_alias_path(dir, dropbox) || stat(dropbox, &sb)) {
#else
   if (!realpath(dir, dropbox) || stat(dropbox, &sb)) {
#endif
default_dropbox:
      dropbox[0] = 0;
   }
}

void
account_getconf (struct htlc_conn *htlc)
{
   struct hxd_config cfg;
   char file[MAXPATHLEN];

   account_getdirs(htlc->login, htlc->rootdir, htlc->newsfile, htlc->dropbox);

   if (snprintf(file, MAXPATHLEN, "%s/%s/conf", hxd_cfg.paths.accounts, htlc->login) == -1) {
      hxd_log("account_getconf(%s): path '%s' too long?!?", htlc->login, file);
      return;
   }
   memset(&cfg, 0, sizeof(cfg));
   cfg.limits.individual_exec = hxd_cfg.limits.individual_exec;
   cfg.limits.individual_downloads = hxd_cfg.limits.individual_downloads;
   cfg.limits.individual_uploads = hxd_cfg.limits.individual_uploads;
   cfg.limits.out_Bps = hxd_cfg.limits.out_Bps;
   /* remote server queueing spec */
   cfg.limits.can_omit_queue = hxd_cfg.limits.can_omit_queue;
   hxd_read_config(file, &cfg);
   htlc->get_limit = cfg.limits.individual_downloads > HTXF_GET_MAX ? HTXF_GET_MAX : cfg.limits.individual_downloads;
   htlc->put_limit = cfg.limits.individual_uploads > HTXF_PUT_MAX  ? HTXF_PUT_MAX : cfg.limits.individual_uploads;
   htlc->limit_out_Bps = cfg.limits.out_Bps;
   htlc->exec_limit = cfg.limits.individual_exec;
   /* remote server queuing spec */
   htlc->can_download = cfg.limits.can_omit_queue;
}

int
access_extra_set (struct extra_access_bits *acc, char *p, int val)
{
   switch (p[0]) {
   case 'a':
      if (!strcmp(p, "access_volatile"))
         { acc->access_volatile = val; return 0; }
   case 'c':
      if (!strcmp(p, "can_spam"))
         { acc->can_spam = val; return 0; }
      else if (!strcmp(p, "chat_private"))
         { acc->chat_private = val; return 0; }
   case 'd':
      if (!strcmp(p, "debug"))
         { acc->debug = val; return 0; }
   case 'f':
      if (!strcmp(p, "file_getinfo"))
         { acc->file_getinfo = val; return 0; }
      else if (!strcmp(p, "file_hash"))
         { acc->file_hash = val; return 0; }
      else if (!strcmp(p, "file_list"))
         { acc->file_list = val; return 0; }
   case 'i':
      if (!strcmp(p, "is_0wn3d"))
         { acc->is_0wn3d = val; return 0; }
      else if (!strcmp(p, "info_get_address"))
         { acc->info_get_address = val; return 0; }
      else if (!strcmp(p, "info_get_login"))
         { acc->info_get_login = val; return 0; }
   case 'm':
      if (!strcmp(p, "manage_users"))
         { acc->manage_users = val; return 0; }
      else if (!strcmp(p, "msg"))
         { acc->msg = val; return 0; }
   case 's':
      if (!strcmp(p, "set_subject"))
         { acc->set_subject = val; return 0; }
   case 'u':
      if (!strcmp(p, "user_0wn"))
         { acc->user_0wn = val; return 0; }
      else if (!strcmp(p, "user_access"))
         { acc->user_access = val; return 0; }
      else if (!strcmp(p, "user_color"))
         { acc->user_color = val; return 0; }
      else if (!strcmp(p, "user_getlist"))
         { acc->user_getlist = val; return 0; }
      else if (!strcmp(p, "user_visibility"))
         { acc->user_visibility = val; return 0; }
   }
   return 1;
}

void
access_extra_set_default (struct login_defaults *def, char *p, u_int16_t val)
{
   if (!strcmp(p, "color")) {
      def->color = val;
      def->has_default_color = 1;
   } else if (!strcmp(p, "icon")) {
      def->icon = val;
      def->has_default_icon = 1;
   }
}

int
set_access_bit (struct hl_access_bits *acc, char *p, int val)
{
   switch (p[0]) {
   case 'c':
      if (!strcmp(p, "cant_be_disconnected"))
         { acc->cant_be_disconnected = val; return 0; }
      else if (!strcmp(p, "comment_files"))
         { acc->comment_files = val; return 0; }
      else if (!strcmp(p, "comment_folders"))
         { acc->comment_folders = val; return 0; }
      else if (!strcmp(p, "create_folders"))
         { acc->create_folders = val; return 0; }
      else if (!strcmp(p, "create_users"))
         { acc->create_users = val; return 0; }
   case 'd':
      if (!strcmp(p, "delete_files"))
         { acc->delete_files = val; return 0; }
      else if (!strcmp(p, "delete_folders"))
         { acc->delete_folders = val; return 0; }
      else if (!strcmp(p, "delete_users"))
         { acc->delete_users = val; return 0; }
      else if (!strcmp(p, "disconnect_users"))
         { acc->disconnect_users = val; return 0; }
      else if (!strcmp(p, "dont_show_agreement"))
         { acc->dont_show_agreement = val; return 0; }
      else if (!strcmp(p, "download_files"))
         { acc->download_files = val; return 0; }
      else if (!strcmp(p, "download_folders"))
         { acc->download_folders = val; return 0; }
   case 'g':
      if (!strcmp(p, "get_user_info"))
         { acc->get_user_info = val; return 0; }
   case 'm':
      if (!strcmp(p, "make_aliases"))
         { acc->make_aliases = val; return 0; }
      else if (!strcmp(p, "modify_users"))
         { acc->modify_users = val; return 0; }
      else if (!strcmp(p, "move_files"))
         { acc->move_files = val; return 0; }
      else if (!strcmp(p, "move_folders"))
         { acc->move_folders = val; return 0; }
   case 'p':
      if (!strcmp(p, "post_news"))
         { acc->post_news = val; return 0; }
   case 'r':
      if (!strcmp(p, "read_chat"))
         { acc->read_chat = val; return 0; }
      else if (!strcmp(p, "read_news"))
         { acc->read_news = val; return 0; }
      else if (!strcmp(p, "read_users"))
         { acc->read_users = val; return 0; }
      else if (!strcmp(p, "rename_files"))
         { acc->rename_files = val; return 0; }
      else if (!strcmp(p, "rename_folders"))
         { acc->rename_folders = val; return 0; }
   case 's':
      if (!strcmp(p, "send_chat"))
         { acc->send_chat = val; return 0; }
   case 'u':
      if (!strcmp(p, "upload_anywhere"))
         { acc->upload_anywhere = val; return 0; }
      else if (!strcmp(p, "upload_files"))
         { acc->upload_files = val; return 0; }
      else if (!strcmp(p, "use_any_name"))
         { acc->use_any_name = val; return 0; }
   case 'v':
      if (!strcmp(p, "view_drop_boxes"))
         { acc->view_drop_boxes = val; return 0; }
   }
   return 1;
}
      
void
account_get_access_extra (struct htlc_conn *htlc)
{
   int fd, r, lastlinelen, val;
   u_int16_t def_val;
   char buf[1024], *p, *lastlinep, *nextp, *ep;
   char path[MAXPATHLEN];

   /* set the default options before reading in real values */
   htlc->access_extra.chat_private = 1;
   htlc->access_extra.msg = 1;
   htlc->access_extra.user_getlist = 1;
   htlc->access_extra.file_list = 1;
   htlc->access_extra.file_getinfo = 1;
   htlc->access_extra.file_hash = 1;
   htlc->access_extra.user_visibility = 0;
   htlc->access_extra.user_color = 0;
   htlc->access_extra.set_subject = 0;
   htlc->access_extra.debug = 0;
   htlc->access_extra.user_access = 0;
   htlc->access_extra.access_volatile = 1;
   htlc->access_extra.user_0wn = 0;
   htlc->access_extra.is_0wn3d = 1;
   htlc->access_extra.manage_users = 0;
   htlc->access_extra.info_get_address = 1;
   htlc->access_extra.info_get_login = 1;

   /* the below is used to track if the color/icon has been set in the access
    * file (better than just testing if it is non-zero) */
   htlc->defaults.has_default_color = 0; /* reserved for internal use */
   htlc->defaults.has_default_icon = 0;  /* reserved for internal use */
   
   if (snprintf(path, MAXPATHLEN, "%s/%s/access", hxd_cfg.paths.accounts, htlc->login) == -1) {
      hxd_log("account_getconf(%s): path '%s' too long?!?", htlc->login, path);
      return;
   }
   if ((fd = open(path, O_RDONLY)) < 0)
      return;
   p = lastlinep = buf;
   for (;;) {
      lastlinelen = p - lastlinep;
      if (lastlinelen) {
         memcpy(buf, lastlinep, lastlinelen);
         if (sizeof(buf)-lastlinelen-1 == 0)
            break;
      }
      r = read(fd, buf+lastlinelen, sizeof(buf)-lastlinelen-1);
      if (r <= 0)
         break;
      buf[r] = 0;
      for (p = buf; *p; p = nextp) {
         nextp = strchr(p, '\n');
         if (nextp) {
            *nextp = 0;
            nextp++;
            lastlinep = nextp;
         } else {
            do p++; while (*p);
            break;
         }
         while (isspace(*p)) p++;
         if (*p == '#')
            continue;
         for (ep = p; *ep; ep++) {
            if (*ep == '=' || isspace(*ep)) {
               if (*ep == '=') {
                  *ep++ = 0;
                  goto is_eq;
               }
               *ep++ = 0;
               while (isspace(*ep)) ep++;
               
               if (*ep != '=')
                  break;
               else
                  *ep++ = 0;
is_eq:
               while (isspace(*ep)) ep++;
               if (!*ep)
                  break;
               val = *ep == '1' ? 1 : 0;
               access_extra_set(&htlc->access_extra, p, val);
               
               def_val = strtoul(ep, 0, 0);
               access_extra_set_default(&htlc->defaults, p, def_val);
            }
         }
      }
   }
   close(fd);
}

int
account_read (const char *login, char *password, char *name, struct hl_access_bits *acc)
{
   struct hl_user_data user_data;
   int fd, r;
   u_int16_t len;
   char path[MAXPATHLEN];

   if (strchr(login, DIRCHAR))
      return EPERM;

   if (snprintf(path, sizeof(path), "%s/%s/UserData", hxd_cfg.paths.accounts, login) == -1) {
      hxd_log("account_read(%s): path '%s' too long?!?", login, path);
      return ENAMETOOLONG;
   }
   fd = open(path, O_RDONLY);
   if (fd < 0) {
      hxd_log("account_read(%s): open(%s): %s", login, path, strerror(errno));
      return errno;
   }
   if ((r = read(fd, &user_data, 734)) != 734) {
      hxd_log("account_read(%s): read(%d, %x, %u) == %d: %s",
          login, fd, &user_data, 734, r, strerror(errno));
      close(fd);
      return errno;
   }
   close(fd);

   if (acc)
      *acc = user_data.access;
   if (name) {
      len = ntohs(user_data.nlen) > 31 ? 31 : ntohs(user_data.nlen);
      memcpy(name, user_data.name, len);
      name[len] = 0;
   }
   if (password) {
      len = ntohs(user_data.plen) > 31 ? 31 : ntohs(user_data.plen);
      hl_decode(password, user_data.password, len);
      password[len] = 0;
   }
   memset(&user_data, 0, sizeof(user_data));
   
   return 0;
}

int
account_write (const char *login, const char *password, const char *name, const struct hl_access_bits *acc)
{
   int fd, r, err;
   char path[MAXPATHLEN];
   struct hl_user_data user_data;
   u_int16_t nlen, llen, plen;
   struct stat sb;

   if (strchr(login, '/'))
      return EPERM;

   if (snprintf(path, sizeof(path), "%s/%s", hxd_cfg.paths.accounts, login) == -1) {
      hxd_log("account_write(%s): path '%s' too long?!?", login, path);
      return ENAMETOOLONG;
   }
   if (stat(path, &sb)) {
      if (mkdir(path, hxd_cfg.permissions.account_directories)) {
         hxd_log("account_write(%s): mkdir(%s): %s", login, path, strerror(errno));
         return errno;
      }
   }
   if (snprintf(path, sizeof(path), "%s/%s/UserData", hxd_cfg.paths.accounts, login) == -1) {
      hxd_log("account_write(%s): path '%s' too long?!?", login, path);
      return ENAMETOOLONG;
   }
   fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, hxd_cfg.permissions.account_files);
   if (fd < 0) {
      hxd_log("account_write(%s): open(%s) %s", login, path, strerror(errno));
      return errno;
   }

   if ((nlen = strlen(name)) > 134) nlen = 134;
   if ((llen = strlen(login)) > 34) llen = 34;
   if ((plen = strlen(password)) > 32) plen = 32;

   memset(&user_data, 0, sizeof(user_data));
   user_data.magic = htonl(0x00010000);
   user_data.access = *acc;
   user_data.nlen = htons(nlen);
   memcpy(user_data.name, name, nlen);
   user_data.llen = htons(llen);
   memcpy(user_data.login, login, llen);
   user_data.plen = htons(plen);
   hl_encode(user_data.password, password, plen);

   err = 0;
   if ((r = write(fd, &user_data, 734)) != 734) {
      err = errno;
      hxd_log("account_write(%s): write(%d, %x, %u) == %d: %s",
          login, fd, &user_data, 734, r, strerror(err));
   } else {
      fsync(fd);
   }
   close(fd);
   memset(&user_data, 0, sizeof(user_data));

   return err;
}

int
account_delete (const char *login)
{
   char path[MAXPATHLEN];

   /* check if the account name has a '/' in it */
   if (strchr(login, '/'))
      return EPERM;


   if (snprintf(path, sizeof(path), "%s/%s/UserData", hxd_cfg.paths.accounts, login) == -1) {
      hxd_log("account_delete: %s: path '%s' too long?!?", login, path);
      return ENAMETOOLONG;
   }
   if (unlink(path)) {
      hxd_log("account_delete: %s: unlink(%s): %s", login, path, strerror(errno));
      return errno;
   }

   return 0;
}
Exemple #4
0
static void
rcv_task_login (struct htlc_conn *htlc, void *secure)
{
	char abuf[HOSTLEN+1];
	u_int32_t uid;
	u_int16_t icon16;
#ifdef CONFIG_HOPE
	u_int16_t hc;
	u_int8_t *p, *mal = 0;
	u_int16_t mal_len = 0;
	u_int16_t sklen = 0, macalglen = 0, secure_login = 0, secure_password = 0;
	u_int8_t password_mac[20];
	u_int8_t login[32];
	u_int16_t llen, pmaclen;
#ifdef CONFIG_CIPHER
	u_int8_t *s_cipher_al = 0, *c_cipher_al = 0;
	u_int16_t s_cipher_al_len = 0, c_cipher_al_len = 0;
	u_int8_t s_cipheralg[32], c_cipheralg[32];
	u_int16_t s_cipheralglen = 0, c_cipheralglen = 0;
	u_int8_t cipheralglist[64];
	u_int16_t cipheralglistlen;
#endif
#ifdef CONFIG_COMPRESS
	u_int8_t *s_compress_al = 0, *c_compress_al = 0;
	u_int16_t s_compress_al_len = 0, c_compress_al_len = 0;
	u_int8_t s_compressalg[32], c_compressalg[32];
	u_int16_t s_compressalglen = 0, c_compressalglen = 0;
	u_int8_t compressalglist[64];
	u_int16_t compressalglistlen;
#endif

	if (secure) {
		dh_start(htlc)
			switch (dh_type) {
				case HTLS_DATA_LOGIN:
					if (dh_len && dh_len == strlen(htlc->macalg) && !memcmp(htlc->macalg, dh_data, dh_len))
						secure_login = 1;
					break;
				case HTLS_DATA_PASSWORD:
					if (dh_len && dh_len == strlen(htlc->macalg) && !memcmp(htlc->macalg, dh_data, dh_len))
						secure_password = 1;
					break;
				case HTLS_DATA_MAC_ALG:
					mal_len = dh_len;
					mal = dh_data;
					break;
#ifdef CONFIG_CIPHER
				case HTLS_DATA_CIPHER_ALG:
					s_cipher_al_len = dh_len;
					s_cipher_al = dh_data;
					break;
				case HTLC_DATA_CIPHER_ALG:
					c_cipher_al_len = dh_len;
					c_cipher_al = dh_data;
					break;
				case HTLS_DATA_CIPHER_MODE:
					break;
				case HTLC_DATA_CIPHER_MODE:
					break;
				case HTLS_DATA_CIPHER_IVEC:
					break;
				case HTLC_DATA_CIPHER_IVEC:
					break;
#endif
#if defined(CONFIG_COMPRESS)
				case HTLS_DATA_COMPRESS_ALG:
					s_compress_al_len = dh_len;
					s_compress_al = dh_data;
					break;
				case HTLC_DATA_COMPRESS_ALG:
					c_compress_al_len = dh_len;
					c_compress_al = dh_data;
					break;
#endif
				case HTLS_DATA_CHECKSUM_ALG:
					break;
				case HTLC_DATA_CHECKSUM_ALG:
					break;
				case HTLS_DATA_SESSIONKEY:
					sklen = dh_len > sizeof(htlc->sessionkey) ? sizeof(htlc->sessionkey) : dh_len;
					memcpy(htlc->sessionkey, dh_data, sklen);
					htlc->sklen = sklen;
					break;
			}
		dh_end()

		if (!mal_len) {
no_mal:			hx_printf_prefix(htlc, 0, INFOPREFIX, "No macalg from server\n");
			hx_htlc_close(htlc);
			memset(htlc->password, 0, sizeof(htlc->password));
			return;
		}

		p = list_n(mal, mal_len, 0);
		if (!p || !*p)
			goto no_mal;
		macalglen = *p >= sizeof(htlc->macalg) ? sizeof(htlc->macalg)-1 : *p;
		memcpy(htlc->macalg, p+1, macalglen);
		htlc->macalg[macalglen] = 0;

		if (sklen < 32) {
			hx_printf_prefix(htlc, 0, INFOPREFIX,
					 "sessionkey length (%u) not big enough\n", sklen);
			hx_htlc_close(htlc);
			memset(htlc->password, 0, sizeof(htlc->password));
			return;
		}
#ifdef CONFIG_IPV6
		if (memcmp(htlc->sessionkey, &htlc->sockaddr.SIN_ADDR.S_ADDR, 16)
		    || *((u_int16_t *)(htlc->sessionkey + 16)) != htlc->sockaddr.SIN_PORT) {
#else
		if (*((u_int32_t *)(htlc->sessionkey)) != htlc->sockaddr.SIN_ADDR.S_ADDR
		    || *((u_int16_t *)(htlc->sessionkey + 4)) != htlc->sockaddr.SIN_PORT) {
#endif
			char fakeabuf[HOSTLEN+1], realabuf[HOSTLEN+1];
			struct IN_ADDR fakeinaddr;

#ifdef CONFIG_IPV6
			memcpy(&fakeinaddr.S_ADDR, htlc->sessionkey, 16);
			inet_ntop(AFINET, (char *)&fakeinaddr, fakeabuf, sizeof(fakeabuf));
			inet_ntop(AFINET, (char *)&htlc->sockaddr.SIN_ADDR, realabuf, sizeof(realabuf));
#else
			fakeinaddr.S_ADDR = *((u_int32_t *)(htlc->sessionkey));
			inet_ntoa_r(fakeinaddr, fakeabuf, sizeof(fakeabuf));
			inet_ntoa_r(htlc->sockaddr.SIN_ADDR, realabuf, sizeof(realabuf));
#endif
			hx_printf_prefix(htlc, 0, INFOPREFIX, "Server gave wrong address: %s:%u != %s:%u\n",
					 fakeabuf, ntohs(*((u_int16_t *)(htlc->sessionkey + 4))),
					 realabuf, ntohs(htlc->sockaddr.SIN_PORT));
			/* XXX HACK XXX */
			if (htlc->secure == 2) {
				hx_printf_prefix(htlc, 0, INFOPREFIX, "Possible man-in-the-middle attack! Connecting anyway.\n");
			} else {
				hx_printf_prefix(htlc, 0, INFOPREFIX, "Possible man-in-the-middle attack! Closing connection. Use -f to force connect.\n");
				hx_htlc_close(htlc);
				memset(htlc->password, 0, sizeof(htlc->password));
				return;
			}
		}
		if (task_inerror(htlc)) {
			hx_htlc_close(htlc);
			memset(htlc->password, 0, sizeof(htlc->password));
			return;
		}
		task_new(htlc, rcv_task_login, 0, 0, "login");
		icon16 = htons(htlc->icon);
		if (secure_login) {
			llen = hmac_xxx(login, htlc->login, strlen(htlc->login),
					htlc->sessionkey, sklen, htlc->macalg);
			if (!llen) {
				hx_printf_prefix(htlc, 0, INFOPREFIX,
						 "bad HMAC algorithm %s\n", htlc->macalg);
				hx_htlc_close(htlc);
				memset(htlc->password, 0, sizeof(htlc->password));
				return;
			}
		} else {
			llen = strlen(htlc->login);
			hl_encode(login, htlc->login, llen);
			login[llen] = 0;
		}
		pmaclen = hmac_xxx(password_mac, htlc->password, strlen(htlc->password),
				   htlc->sessionkey, sklen, htlc->macalg);
		if (!pmaclen) {	
			hx_printf_prefix(htlc, 0, INFOPREFIX,
					 "bad HMAC algorithm %s\n", htlc->macalg);
			hx_htlc_close(htlc);
			return;
		}
		hc = 4;
#ifdef CONFIG_COMPRESS
		if (!htlc->compressalg[0] || !strcmp(htlc->compressalg, "NONE")) {
			hx_printf_prefix(htlc, 0, INFOPREFIX,
					 "WARNING: this connection is not compressed\n");
			compressalglistlen = 0;
			goto no_compress;
		}
		if (!c_compress_al_len || !s_compress_al_len) {
no_compress_al:		hx_printf_prefix(htlc, 0, INFOPREFIX,
				 "No compress algorithm from server\n");
			hx_htlc_close(htlc);
			return;
		}
		p = list_n(s_compress_al, s_compress_al_len, 0);
		if (!p || !*p)
			goto no_compress_al;
		s_compressalglen = *p >= sizeof(s_compressalg) ? sizeof(s_compressalg)-1 : *p;
		memcpy(s_compressalg, p+1, s_compressalglen);
		s_compressalg[s_compressalglen] = 0;
		p = list_n(c_compress_al, c_compress_al_len, 0);
		if (!p || !*p)
			goto no_compress_al;
		c_compressalglen = *p >= sizeof(c_compressalg) ? sizeof(c_compressalg)-1 : *p;
		memcpy(c_compressalg, p+1, c_compressalglen);
		c_compressalg[c_compressalglen] = 0;
		if (!valid_compress(c_compressalg)) {
			hx_printf_prefix(htlc, 0, INFOPREFIX,
					 "Bad client compress algorithm %s\n", c_compressalg);
			goto ret_badcompress_a;
		} else if (!valid_compress(s_compressalg)) {
			hx_printf_prefix(htlc, 0, INFOPREFIX,
					 "Bad server compress algorithm %s\n", s_compressalg);
ret_badcompress_a:
			compressalglistlen = 0;
			hx_htlc_close(htlc);
			return;
		} else {
			S16HTON(1, compressalglist);
			compressalglistlen = 2;
			compressalglist[compressalglistlen] = s_compressalglen;
			compressalglistlen++;
			memcpy(compressalglist+compressalglistlen, s_compressalg, s_compressalglen);
			compressalglistlen += s_compressalglen;
		}
no_compress:
		hc++;
#endif
#ifdef CONFIG_CIPHER
		if (!htlc->cipheralg[0] || !strcmp(htlc->cipheralg, "NONE")) {
			hx_printf_prefix(htlc, 0, INFOPREFIX,
					 "WARNING: this connection is not encrypted\n");
			cipheralglistlen = 0;
			goto no_cipher;
		}
		if (!c_cipher_al_len || !s_cipher_al_len) {
no_cal:			hx_printf_prefix(htlc, 0, INFOPREFIX,
					 "No cipher algorithm from server\n");
			hx_htlc_close(htlc);
			return;
		}
		p = list_n(s_cipher_al, s_cipher_al_len, 0);
		if (!p || !*p)
			goto no_cal;
		s_cipheralglen = *p >= sizeof(s_cipheralg) ? sizeof(s_cipheralg)-1 : *p;
		memcpy(s_cipheralg, p+1, s_cipheralglen);
		s_cipheralg[s_cipheralglen] = 0;
		p = list_n(c_cipher_al, c_cipher_al_len, 0);
		if (!p || !*p)
			goto no_cal;
		c_cipheralglen = *p >= sizeof(c_cipheralg) ? sizeof(c_cipheralg)-1 : *p;
		memcpy(c_cipheralg, p+1, c_cipheralglen);
		c_cipheralg[c_cipheralglen] = 0;
		if (!valid_cipher(c_cipheralg)) {
			hx_printf_prefix(htlc, 0, INFOPREFIX,
					 "Bad client cipher algorithm %s\n", c_cipheralg);
			goto ret_badca;
		} else if (!valid_cipher(s_cipheralg)) {
			hx_printf_prefix(htlc, 0, INFOPREFIX,
					 "Bad server cipher algorithm %s\n", s_cipheralg);
ret_badca:
			cipheralglistlen = 0;
			hx_htlc_close(htlc);
			return;
		} else {
			S16HTON(1, cipheralglist);
			cipheralglistlen = 2;
			cipheralglist[cipheralglistlen] = s_cipheralglen;
			cipheralglistlen++;
			memcpy(cipheralglist+cipheralglistlen, s_cipheralg, s_cipheralglen);
			cipheralglistlen += s_cipheralglen;
		}

		/* server key first */
		pmaclen = hmac_xxx(htlc->cipher_decode_key, htlc->password, strlen(htlc->password),
				   password_mac, pmaclen, htlc->macalg);
		htlc->cipher_decode_keylen = pmaclen;
		pmaclen = hmac_xxx(htlc->cipher_encode_key, htlc->password, strlen(htlc->password),
				   htlc->cipher_decode_key, pmaclen, htlc->macalg);
		htlc->cipher_encode_keylen = pmaclen;
no_cipher:
		hc++;
#endif
		memset(htlc->password, 0, sizeof(htlc->password));
		hlwrite(htlc, HTLC_HDR_LOGIN, 0, hc,
			HTLC_DATA_LOGIN, llen, login,
			HTLC_DATA_PASSWORD, pmaclen, password_mac,
#ifdef CONFIG_CIPHER
			HTLS_DATA_CIPHER_ALG, cipheralglistlen, cipheralglist,
#endif
#ifdef CONFIG_COMPRESS
			HTLS_DATA_COMPRESS_ALG, compressalglistlen, compressalglist,
#endif
			HTLC_DATA_NAME, strlen(htlc->name), htlc->name,
			HTLC_DATA_ICON, 2, &icon16);
#ifdef CONFIG_COMPRESS
		if (compressalglistlen) {
			hx_printf_prefix(htlc, 0, INFOPREFIX, "compress: server %s client %s\n",
					 c_compressalg, s_compressalg);
			if (c_compress_al_len) {
				htlc->compress_encode_type = COMPRESS_GZIP;
				compress_encode_init(htlc);
			}
			if (s_compress_al_len) {
				htlc->compress_decode_type = COMPRESS_GZIP;
				compress_decode_init(htlc);
			}
		}
#endif
#ifdef CONFIG_CIPHER
		if (cipheralglistlen) {
			hx_printf_prefix(htlc, 0, INFOPREFIX, "cipher: server %s client %s\n",
					 c_cipheralg, s_cipheralg);
			if (!strcmp(s_cipheralg, "RC4"))
				htlc->cipher_decode_type = CIPHER_RC4;
			else if (!strcmp(s_cipheralg, "BLOWFISH"))
				htlc->cipher_decode_type = CIPHER_BLOWFISH;
			else if (!strcmp(s_cipheralg, "IDEA"))
				htlc->cipher_decode_type = CIPHER_IDEA;
			if (!strcmp(c_cipheralg, "RC4"))
				htlc->cipher_encode_type = CIPHER_RC4;
			else if (!strcmp(c_cipheralg, "BLOWFISH"))
				htlc->cipher_encode_type = CIPHER_BLOWFISH;
			else if (!strcmp(c_cipheralg, "IDEA"))
				htlc->cipher_encode_type = CIPHER_IDEA;
			cipher_encode_init(htlc);
			cipher_decode_init(htlc);
		}
#endif
	} else {
#endif /* CONFIG_HOPE */
		inaddr2str(abuf, &htlc->sockaddr);
		hx_printf_prefix(htlc, 0, INFOPREFIX, "%s:%u: login %s\n",
			  abuf, ntohs(htlc->sockaddr.SIN_PORT), task_inerror(htlc) ? "failed?" : "successful");

		if (!task_inerror(htlc)) {
			hx_chat_new(htlc, 0);
			play_sound(snd_login);
			dh_start(htlc)
				switch (dh_type) {
					case HTLS_DATA_UID:
						dh_getint(uid);
						htlc->uid = uid;
						break;
					case HTLS_DATA_SERVERVERSION:
						dh_getint(htlc->serverversion);
						break;
					case HTLS_DATA_BANNERID:
						break;
					case HTLS_DATA_SERVERNAME:
						hx_printf_prefix(htlc, 0, INFOPREFIX, "servername: %.*s\n", dh_len, dh_data);
						break;
				}
			dh_end()
			if (htlc->clientversion >= 150 && htlc->serverversion < 150) {
				icon16 = htons(htlc->icon);
				hlwrite(htlc, HTLC_HDR_USER_CHANGE, 0, 2,
					HTLC_DATA_NAME, strlen(htlc->name), htlc->name,
					HTLC_DATA_ICON, 2, &icon16);
			}
		hx_output.on_connect(htlc);
		}
#ifdef CONFIG_HOPE
	}
#endif
}