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() }
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)); }
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; }
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 }