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