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, ¤t_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