1 /* Secure Shout Host Oriented Unified Talk
2  * Copyright 2015-2019 Rivoreo
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation, either version 3 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  */
14 
15 #include "common.h"
16 #include "client.h"
17 #include "misc.h"
18 #include <sys/socket.h>
19 #include <sys/un.h>
20 #include <unistd.h>
21 #include <syslog.h>
22 #include "syncrw.h"
23 #include <string.h>
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #ifndef NO_NLS
30 #include <locale.h>
31 #endif
32 
33 #define MAX(A,B) ((A)>(B)?(A):(B))
34 #define REMOTE_MODE_CLI 1
35 #define REMOTE_MODE_API 2
36 #define REMOTE_MODE_LOG 3
37 #define REMOTE_MODE_IRC 4
38 
send_login(int fd,const char * orig_user_name,const char * client_address)39 static int send_login(int fd, const char *orig_user_name, const char *client_address) {
40 	int r = 0;
41 	size_t user_name_len = strlen(orig_user_name);
42 	//char user_name[USER_NAME_MAX_LENGTH];
43 	if(user_name_len > USER_NAME_MAX_LENGTH - 1) user_name_len = USER_NAME_MAX_LENGTH - 1;
44 	//memcpy(user_name, orig_user_name, user_name_len);
45 	//memset(user_name + user_name_len, 0, USER_NAME_MAX_LENGTH - user_name_len);
46 	const char *space = strchr(client_address, ' ');
47 	size_t host_name_len = space ? space - client_address : strlen(client_address);
48 	if(!host_name_len) {
49 		errno = EINVAL;
50 		return -1;
51 	}
52 	if(host_name_len > HOST_NAME_MAX_LENGTH - 1) host_name_len = HOST_NAME_MAX_LENGTH - 1;
53 	//char host_name[host_name_len + 1];
54 	//memcpy(host_name, client_address, host_name_len);
55 	//host_name[host_name_len] = 0;
56 	size_t packet_len = sizeof(struct local_packet) + USER_NAME_MAX_LENGTH + host_name_len + 1;
57 	struct local_packet *packet = malloc(packet_len);
58 	if(!packet) return -1;
59 	packet->length = packet_len - sizeof packet->length;
60 	packet->type = SSHOUT_LOCAL_LOGIN;
61 	memcpy(packet->data, orig_user_name, user_name_len);
62 	memset(packet->data + user_name_len, 0, USER_NAME_MAX_LENGTH - user_name_len);
63 	char *host_name = packet->data + USER_NAME_MAX_LENGTH;
64 	memcpy(host_name, client_address, host_name_len);
65 	host_name[host_name_len] = 0;
66 	while(write(fd, packet, packet_len) < 0) {
67 		if(errno == EINTR) continue;
68 		r = -1;
69 		break;
70 	}
71 	int e = errno;
72 	free(packet);
73 	errno = e;
74 	return r;
75 }
76 
client_send_request_get_online_users(int fd)77 int client_send_request_get_online_users(int fd) {
78 	struct local_packet packet;
79 	packet.length = sizeof packet - sizeof packet.length;
80 	packet.type = SSHOUT_LOCAL_GET_ONLINE_USERS;
81 	while(write(fd, &packet, sizeof packet) < 0) {
82 		if(errno == EINTR) continue;
83 		return -1;
84 	}
85 	return 0;
86 }
87 
88 // field msg_from is not needed
client_post_message(int fd,const struct local_message * message)89 int client_post_message(int fd, const struct local_message *message) {
90 	int r = 0;
91 	size_t packet_len = sizeof(struct local_packet) + sizeof(struct local_message) + message->msg_length;
92 	if(packet_len < message->msg_length) {
93 #ifdef EOVERFLOW
94 		errno = EOVERFLOW;
95 #else
96 		errno = EINVAL;
97 #endif
98 		return -1;
99 	}
100 	struct local_packet *packet = malloc(packet_len);
101 	if(!packet) return -1;
102 	packet->length = packet_len - sizeof packet->length;
103 	packet->type = SSHOUT_LOCAL_POST_MESSAGE;
104 	memcpy(packet->data, message, sizeof(struct local_message) + message->msg_length);
105 	if(sync_write(fd, packet, packet_len) < 0) {
106 		r = -1;
107 	}
108 	int e = errno;
109 	free(packet);
110 	errno = e;
111 	return r;
112 }
113 
client_post_plain_text_message(int fd,const char * receiver,const char * text)114 int client_post_plain_text_message(int fd, const char *receiver, const char *text) {
115 	size_t receiver_len = strlen(receiver);
116 	size_t text_len = strlen(text);
117 	struct local_message *message = malloc(sizeof(struct local_message) + text_len);
118 	if(!message) return -1;
119 	if(receiver_len > USER_NAME_MAX_LENGTH - 1) receiver_len = USER_NAME_MAX_LENGTH - 1;
120 	memcpy(message->msg_to, receiver, receiver_len);
121 	message->msg_to[receiver_len] = 0;
122 	message->msg_type = SSHOUT_MSG_PLAIN;
123 	message->msg_length = text_len;
124 	memcpy(message->msg, text, text_len);
125 	int r = client_post_message(fd, message);
126 	free(message);
127 	return r;
128 }
129 
130 static int local_socket = -1;
131 
client_get_local_socket_fd()132 int client_get_local_socket_fd() {
133 	return local_socket;
134 }
135 
client_mode(const struct sockaddr_un * socket_addr,const char * user_name)136 int client_mode(const struct sockaddr_un *socket_addr, const char *user_name) {
137 	pid_t ppid = getppid();
138 	int remote_mode = REMOTE_MODE_CLI;
139 
140 	const char *client_address = getenv("SSH_CLIENT");
141 	if(!client_address) {
142 		fputs("client mode can only be used in a SSH session\n", stderr);
143 		return 1;
144 	}
145 
146 #ifndef NO_NLS
147 	setlocale(LC_ALL, "");
148 	textdomain("sshout");
149 #endif
150 
151 	if(!is_valid_user_name(user_name)) {
152 		fprintf(stderr, _("Invalid user name '%s'\n"), user_name);
153 		return 1;
154 	}
155 	const char *command = getenv("SSH_ORIGINAL_COMMAND");
156 	if(command) {
157 		if(strcmp(command, "cli") == 0) remote_mode = REMOTE_MODE_CLI;
158 		else if(strcmp(command, "api") == 0) remote_mode = REMOTE_MODE_API;
159 		else if(strcmp(command, "log") == 0) remote_mode = REMOTE_MODE_LOG;
160 #ifdef ENABLE_IRC_FRONTEND
161 		else if(strcmp(command, "irc") == 0) remote_mode = REMOTE_MODE_IRC;
162 #endif
163 		else {
164 			fprintf(stderr, _("Command '%s' is not recognized\n"), command);
165 			return 1;
166 		}
167 	}
168 
169 	size_t len = 8 + USER_NAME_MAX_LENGTH + 4 + 1;
170 	char *syslog_ident = malloc(len);
171 	if(!syslog_ident) {
172 		perror("malloc");
173 		exit(1);
174 	}
175 	snprintf(syslog_ident, len, "sshoutd:%s:%s", user_name, command ? : "cli");
176 	openlog(syslog_ident, LOG_PID, LOG_DAEMON);
177 
178 	char *tz = getenv("TZ");
179 	if(tz) {
180 		if((tz[0] == ':' && tz[1] == '/') || tz[0] == '/') {
181 			fputs(_("Ignoring absolute path name in TZ\n"), stderr);
182 			syslog(LOG_WARNING, "TZ contains absolute path name '%s', ignored", tz);
183 			*tz = 0;
184 		} else if(strstr(tz, "../")) {
185 			fputs(_("Ignoring TZ\n"), stderr);
186 			syslog(LOG_WARNING, "TZ='%s', ignored", tz);
187 			*tz = 0;
188 		}
189 	}
190 
191 	int fd = socket(AF_UNIX, SOCK_STREAM, 0);
192 	if(fd == -1) {
193 		perror("socket");
194 		return 1;
195 	}
196 	while(connect(fd, (struct sockaddr *)socket_addr, sizeof(struct sockaddr_un)) < 0) {
197 		if(errno == EINTR) continue;
198 		perror("connect");
199 		return 1;
200 	}
201 
202 	if(send_login(fd, user_name, client_address) < 0) {
203 		perror("send_login");
204 		return 1;
205 	}
206 
207 	struct timeval timeout = { .tv_sec = 60 };
208 	fd_set fdset;
209 	FD_ZERO(&fdset);
210 	FD_SET(fd, &fdset);
211 	int max_fd;
212 	if(remote_mode == REMOTE_MODE_LOG) {
213 		max_fd = fd;
214 		close(STDIN_FILENO);
215 		if(open("/dev/null", O_RDONLY) != 0) {
216 			fputs(_("Cannot open /dev/null for read as fd 0\n"), stderr);
217 			syslog(LOG_WARNING, "Cannot open /dev/null for read as fd 0\n");
218 			return 1;
219 		}
220 	} else {
221 		FD_SET(STDIN_FILENO, &fdset);
222 		max_fd = MAX(fd, STDIN_FILENO);
223 	}
224 
225 	struct client_frontend_actions actions;
226 	switch(remote_mode) {
227 		case REMOTE_MODE_CLI:
228 			client_cli_get_actions(&actions, 0);
229 			break;
230 		case REMOTE_MODE_API:
231 			client_api_get_actions(&actions);
232 			break;
233 		case REMOTE_MODE_LOG:
234 			client_cli_get_actions(&actions, 1);
235 			break;
236 #ifdef ENABLE_IRC_FRONTEND
237 		case REMOTE_MODE_IRC:
238 			client_irc_get_actions(&actions);
239 			break;
240 #endif
241 	}
242 	actions.init_io(user_name);
243 	local_socket = fd;
244 
245 	while(getppid() == ppid) {
246 		fd_set rfdset = fdset;
247 		struct timeval current_timeout = timeout;
248 		int n = select(max_fd + 1, &rfdset, NULL, NULL, &current_timeout);
249 		if(n < 0) {
250 			if(errno == EINTR) {
251 				if(actions.do_after_signal) actions.do_after_signal();
252 				continue;
253 			}
254 			perror("select");
255 			return 1;
256 		}
257 		if(actions.do_tick) actions.do_tick();
258 		if(n) {
259 			if(FD_ISSET(fd, &rfdset)) actions.do_local_packet(fd);
260 			if(FD_ISSET(STDIN_FILENO, &rfdset)) actions.do_stdin(fd);
261 		}
262 	}
263 
264 	//fputs(_("Parent process changed, exiting\n"), stderr);
265 	return 0;
266 }
267