xref: /freebsd/contrib/sendmail/src/control.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  *
4  * By using this file, you agree to the terms and conditions set
5  * forth in the LICENSE file which can be found at the top level of
6  * the sendmail distribution.
7  *
8  */
9 
10 #ifndef lint
11 static char sccsid[] = "@(#)control.c	8.18 (Berkeley) 1/17/1999";
12 #endif /* not lint */
13 
14 #include "sendmail.h"
15 
16 int ControlSocket = -1;
17 
18 /*
19 **  OPENCONTROLSOCKET -- create/open the daemon control named socket
20 **
21 **	Creates and opens a named socket for external control over
22 **	the sendmail daemon.
23 **
24 **	Parameters:
25 **		none.
26 **
27 **	Returns:
28 **		0 if successful, -1 otherwise
29 */
30 
31 int
32 opencontrolsocket()
33 {
34 #ifdef NETUNIX
35 # if _FFR_CONTROL_SOCKET
36 	int rval;
37 	int sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
38 	struct sockaddr_un controladdr;
39 
40 	if (ControlSocketName == NULL)
41 		return 0;
42 
43 	if (strlen(ControlSocketName) >= sizeof controladdr.sun_path)
44 	{
45 		errno = ENAMETOOLONG;
46 		return -1;
47 	}
48 
49 	rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
50 			sff, S_IRUSR|S_IWUSR, NULL);
51 
52 	/* if not safe, don't create */
53 	if (rval != 0)
54 	{
55 		errno = rval;
56 		return -1;
57 	}
58 
59 	ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0);
60 	if (ControlSocket < 0)
61 		return -1;
62 
63 	unlink(ControlSocketName);
64 	bzero(&controladdr, sizeof controladdr);
65 	controladdr.sun_family = AF_UNIX;
66 	strcpy(controladdr.sun_path, ControlSocketName);
67 
68 	if (bind(ControlSocket, (struct sockaddr *) &controladdr,
69 		 sizeof controladdr) < 0)
70 	{
71 		int save_errno = errno;
72 
73 		clrcontrol();
74 		errno = save_errno;
75 		return -1;
76 	}
77 
78 #  if _FFR_TRUSTED_USER
79 	if (geteuid() == 0 && TrustedUid != 0)
80 	{
81 		if (chown(ControlSocketName, TrustedUid, -1) < 0)
82 		{
83 			int save_errno = errno;
84 
85 			sm_syslog(LOG_ALERT, NOQID,
86 				  "ownership change on %s failed: %s",
87 				  ControlSocketName, errstring(save_errno));
88 			message("050 ownership change on %s failed: %s",
89 				ControlSocketName, errstring(save_errno));
90 			closecontrolsocket(TRUE);
91 			errno = save_errno;
92 			return -1;
93 		}
94 	}
95 #  endif
96 
97 	if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
98 	{
99 		int save_errno = errno;
100 
101 		closecontrolsocket(TRUE);
102 		errno = save_errno;
103 		return -1;
104 	}
105 
106 	if (listen(ControlSocket, 8) < 0)
107 	{
108 		int save_errno = errno;
109 
110 		closecontrolsocket(TRUE);
111 		errno = save_errno;
112 		return -1;
113 	}
114 # endif
115 #endif
116 	return 0;
117 }
118 /*
119 **  CLOSECONTROLSOCKET -- close the daemon control named socket
120 **
121 **	Close a named socket.
122 **
123 **	Parameters:
124 **		fullclose -- if set, close the socket and remove it;
125 **			     otherwise, just remove it
126 **
127 **	Returns:
128 **		none.
129 */
130 
131 void
132 closecontrolsocket(fullclose)
133 	bool fullclose;
134 {
135 #ifdef NETUNIX
136 # if _FFR_CONTROL_SOCKET
137 	int sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
138 
139 	if (ControlSocket >= 0)
140 	{
141 		int rval;
142 
143 		if (fullclose)
144 		{
145 			(void) close(ControlSocket);
146 			ControlSocket = -1;
147 		}
148 
149 		rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
150 				sff, S_IRUSR|S_IWUSR, NULL);
151 
152 		/* if not safe, don't unlink */
153 		if (rval != 0)
154 			return;
155 
156 		if (unlink(ControlSocketName) < 0)
157 		{
158 			sm_syslog(LOG_WARNING, NOQID,
159 				  "Could not remove control socket: %s",
160 				  errstring(errno));
161 			return;
162 		}
163 	}
164 # endif
165 #endif
166 	return;
167 }
168 /*
169 **  CLRCONTROL -- reset the control connection
170 **
171 **	Parameters:
172 **		none.
173 **
174 **	Returns:
175 **		none.
176 **
177 **	Side Effects:
178 **		releases any resources used by the control interface.
179 */
180 
181 void
182 clrcontrol()
183 {
184 #ifdef NETUNIX
185 # if _FFR_CONTROL_SOCKET
186 	if (ControlSocket >= 0)
187 		(void) close(ControlSocket);
188 	ControlSocket = -1;
189 # endif
190 #endif
191 }
192 
193 #ifndef NOT_SENDMAIL
194 
195 /*
196 **  CONTROL_COMMAND -- read and process command from named socket
197 **
198 **	Read and process the command from the opened socket.
199 **	Return the results down the same socket.
200 **
201 **	Parameters:
202 **		sock -- the opened socket from getrequests()
203 **		e -- the current envelope
204 **
205 **	Returns:
206 **		none.
207 */
208 
209 struct cmd
210 {
211 	char	*cmdname;	/* command name */
212 	int	cmdcode;	/* internal code, see below */
213 };
214 
215 /* values for cmdcode */
216 # define CMDERROR	0	/* bad command */
217 # define CMDRESTART	1	/* restart daemon */
218 # define CMDSHUTDOWN	2	/* end daemon */
219 # define CMDHELP	3	/* help */
220 # define CMDSTATUS	4	/* daemon status */
221 
222 static struct cmd	CmdTab[] =
223 {
224 	{ "help",	CMDHELP		},
225 	{ "restart",	CMDRESTART	},
226 	{ "shutdown",	CMDSHUTDOWN	},
227 	{ "status",	CMDSTATUS	},
228 	{ NULL,		CMDERROR	}
229 };
230 
231 void
232 control_command(sock, e)
233 	int sock;
234 	ENVELOPE *e;
235 {
236 	FILE *s;
237 	FILE *traffic;
238 	FILE *oldout;
239 	char *cmd;
240 	char *p;
241 	struct cmd *c;
242 	char cmdbuf[MAXLINE];
243 	char inp[MAXLINE];
244 	extern char **SaveArgv;
245 	extern void help __P((char *));
246 
247 	sm_setproctitle(FALSE, "control cmd read");
248 
249 	s = fdopen(sock, "r+");
250 	if (s == NULL)
251 	{
252 		int save_errno = errno;
253 
254 		close(sock);
255 		errno = save_errno;
256 		return;
257 	}
258 	setbuf(s, NULL);
259 
260 	if (fgets(inp, sizeof inp, s) == NULL)
261 	{
262 		fclose(s);
263 		return;
264 	}
265 	(void) fflush(s);
266 
267 	/* clean up end of line */
268 	fixcrlf(inp, TRUE);
269 
270 	sm_setproctitle(FALSE, "control: %s", inp);
271 
272 	/* break off command */
273 	for (p = inp; isascii(*p) && isspace(*p); p++)
274 		continue;
275 	cmd = cmdbuf;
276 	while (*p != '\0' &&
277 	       !(isascii(*p) && isspace(*p)) &&
278 	       cmd < &cmdbuf[sizeof cmdbuf - 2])
279 		*cmd++ = *p++;
280 	*cmd = '\0';
281 
282 	/* throw away leading whitespace */
283 	while (isascii(*p) && isspace(*p))
284 		p++;
285 
286 	/* decode command */
287 	for (c = CmdTab; c->cmdname != NULL; c++)
288 	{
289 		if (!strcasecmp(c->cmdname, cmdbuf))
290 			break;
291 	}
292 
293 	switch (c->cmdcode)
294 	{
295 	  case CMDHELP:		/* get help */
296 		traffic = TrafficLogFile;
297 		TrafficLogFile = NULL;
298 		oldout = OutChannel;
299 		OutChannel = s;
300 		help("control");
301 		TrafficLogFile = traffic;
302 		OutChannel = oldout;
303 		break;
304 
305 	  case CMDRESTART:	/* restart the daemon */
306 		if (SaveArgv[0][0] != '/')
307 		{
308 			fprintf(s, "ERROR: could not restart: need full path\r\n");
309 			break;
310 		}
311 		if (LogLevel > 3)
312 			sm_syslog(LOG_INFO, NOQID,
313 				  "restarting %s on due to control command",
314 				  SaveArgv[0]);
315 		closecontrolsocket(FALSE);
316 		if (drop_privileges(TRUE) != EX_OK)
317 		{
318 			if (LogLevel > 0)
319 				sm_syslog(LOG_ALERT, NOQID,
320 					  "could not set[ug]id(%d, %d): %m",
321 					  RunAsUid, RunAsGid);
322 
323 			fprintf(s, "ERROR: could not set[ug]id(%d, %d): %s, exiting...\r\n",
324 				(int)RunAsUid, (int)RunAsGid, errstring(errno));
325 			finis(FALSE, EX_OSERR);
326 		}
327 		fprintf(s, "OK\r\n");
328 		clrcontrol();
329 		(void) fcntl(sock, F_SETFD, 1);
330 		execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
331 		if (LogLevel > 0)
332 			sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m",
333 				  SaveArgv[0]);
334 		fprintf(s, "ERROR: could not exec %s: %s, exiting...\r\n",
335 			SaveArgv[0], errstring(errno));
336 		finis(FALSE, EX_OSFILE);
337 		break;
338 
339 	  case CMDSHUTDOWN:	/* kill the daemon */
340 		fprintf(s, "OK\r\n");
341 		finis(FALSE, EX_OK);
342 		break;
343 
344 	  case CMDSTATUS:	/* daemon status */
345 		proc_list_probe();
346 		fprintf(s, "%d/%d\r\n", CurChildren, MaxChildren);
347 		proc_list_display(s);
348 		break;
349 
350 	  case CMDERROR:	/* unknown command */
351 		fprintf(s, "Bad command (%s)\r\n", cmdbuf);
352 		break;
353 	}
354 	fclose(s);
355 }
356 #endif
357