1 /* SAMHAIN file system integrity testing                                   */
2 /* Copyright (C) 2003,2005 Rainer Wichmann                                 */
3 /*                                                                         */
4 /*  This program is free software; you can redistribute it                 */
5 /*  and/or modify                                                          */
6 /*  it under the terms of the GNU General Public License as                */
7 /*  published by                                                           */
8 /*  the Free Software Foundation; either version 2 of the License, or      */
9 /*  (at your option) any later version.                                    */
10 /*                                                                         */
11 /*  This program is distributed in the hope that it will be useful,        */
12 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
13 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
14 /*  GNU General Public License for more details.                           */
15 /*                                                                         */
16 /*  You should have received a copy of the GNU General Public License      */
17 /*  along with this program; if not, write to the Free Software            */
18 /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
19 
20 #include "config_xor.h"
21 
22 /* define if you want debug info
23  * #define SH_DEBUG_SOCKET
24  */
25 
26 #if defined(SH_WITH_SERVER) && defined(__linux__)
27 #define _GNU_SOURCE
28 #endif
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stddef.h>
33 #include <string.h>
34 
35 #if TIME_WITH_SYS_TIME
36 #include <sys/time.h>
37 #include <time.h>
38 #else
39 #if HAVE_SYS_TIME_H
40 #include <sys/time.h>
41 #else
42 #include <time.h>
43 #endif
44 #endif
45 
46 #include "samhain.h"
47 #include "sh_socket.h"
48 #include "sh_error.h"
49 #include "sh_unix.h"
50 #include "sh_calls.h"
51 #include "sh_guid.h"
52 #include "sh_fifo.h"
53 #include "sh_utils.h"
54 
55 #undef  FIL__
56 #define FIL__  _("sh_socket.c")
57 
58 #if defined (SH_WITH_CLIENT)
59 
60 #include <signal.h>
61 
62 typedef struct delta_tofetch {
63   char            uuid[SH_UUID_BUF];
64   time_t          last_time;
65   unsigned int    count;
66 } SH_DELTA_DB;
67 
parse_entry(SH_DELTA_DB * db,const char * str)68 static SH_DELTA_DB * parse_entry(SH_DELTA_DB * db, const char * str)
69 {
70   long last_time;
71   unsigned int count;
72   char buf[SH_UUID_BUF];
73   int res = sscanf(str, _("%u:%ld:%36s"), &count, &last_time, buf);
74   if (res == 3)
75     {
76       db->count = count;
77       db->last_time  = (time_t) last_time;
78       sl_strlcpy(db->uuid, buf, SH_UUID_BUF);
79       return db;
80     }
81   return NULL;
82 }
83 
unparse_entry(const SH_DELTA_DB * db,char * str,size_t len)84 static char * unparse_entry(const SH_DELTA_DB * db, char * str, size_t len)
85 {
86   int nbytes = sl_snprintf(str, len, _("%u:%ld:%s"),
87 			   db->count, (long) db->last_time, db->uuid);
88   if (nbytes < 0 || nbytes >= (int) len)
89     return NULL;
90   return str;
91 }
92 
93 static SH_FIFO xfifo = SH_FIFO_INITIALIZER;
94 
sh_socket_store_uuid(const char * cmd)95 int sh_socket_store_uuid(const char * cmd)
96 {
97   char * p = sh_util_strdup(cmd);
98   char * q = strchr(cmd, ':');
99   char   entry[SH_BUFSIZE];
100   SH_DELTA_DB db;
101 
102   if (!q) { SH_FREE(p); return -1; }
103 
104   ++q;
105 
106   if (0 != sh_uuid_check(q)) { SH_FREE(p); return -1; }
107 
108   db.count = 0;
109   db.last_time = (time_t) 0;
110   sl_strlcpy(db.uuid, q, SH_UUID_BUF);
111   SH_FREE(p);
112 
113   if (NULL != unparse_entry(&db, entry, sizeof(entry)))
114     {
115       sh_fifo_push(&xfifo, entry);
116       return 0;
117     }
118   return -1;
119 }
120 
121 static unsigned int try_interval = 60;
122 static unsigned int try_max = 2;
123 
set_delta_retry_interval(const char * str)124 int set_delta_retry_interval(const char * str)
125 {
126   long val = strtol (str, (char **)NULL, 10);
127 
128   if (val < 0 || val > INT_MAX)
129     return -1;
130   try_interval = (unsigned int) val;
131   return 0;
132 }
set_delta_retry_count(const char * str)133 int set_delta_retry_count(const char * str)
134 {
135   long val = strtol (str, (char **)NULL, 10);
136 
137   if (val < 0 || val > INT_MAX)
138     return -1;
139   try_max = (unsigned int) val;
140   return 0;
141 }
142 
sh_socket_get_uuid(int * errflag,unsigned int * count,time_t * last)143 char * sh_socket_get_uuid(int * errflag, unsigned int * count, time_t * last)
144 {
145   char * entry = sh_fifo_pop(&xfifo);
146   char * uuid = NULL;
147 
148   if (entry)
149     {
150       SH_DELTA_DB db;
151       time_t      now;
152 
153       if (NULL == parse_entry(&db, entry))
154 	{
155 	  SH_FREE(entry);
156 	  sh_error_handle(SH_ERR_WARN, FIL__, __LINE__, 0, MSG_E_SUBGEN,
157 			  _("Bad entry in fifo"),
158 			  _("sh_socket_get_uuid"));
159 	  *errflag = -1;
160 	  return NULL;
161 	}
162 
163       now = time(NULL);
164 
165       if ( (db.count > 0) && ((unsigned long)(now - db.last_time) < try_interval) )
166 	{
167 	  sh_fifo_push_tail(&xfifo, entry);
168 	  SH_FREE(entry);
169 	  *errflag = -1;
170 	  return NULL;
171 	}
172 
173       SH_FREE(entry);
174       uuid   = sh_util_strdup(db.uuid);
175       *count = db.count;
176       *last  = db.last_time;
177     }
178 
179   *errflag = 0;
180   return uuid;
181 }
182 
sh_socket_return_uuid(const char * uuid,unsigned int count,time_t last)183 int sh_socket_return_uuid (const char * uuid, unsigned int count, time_t last)
184 {
185   (void) last;
186 
187   if (count < try_max)
188     {
189       char   entry[SH_BUFSIZE];
190       SH_DELTA_DB db;
191       time_t now = time(NULL);
192 
193       db.count     = count + 1;
194       db.last_time = now;
195       sl_strlcpy(db.uuid, uuid, SH_UUID_BUF);
196 
197       if (NULL != unparse_entry(&db, entry, sizeof(entry)))
198 	return sh_fifo_push_tail(&xfifo, entry); /* >0 for success */
199     }
200   return -1;
201 }
202 
sh_socket_server_cmd(const char * srvcmd)203 void sh_socket_server_cmd(const char * srvcmd)
204 {
205   SL_ENTER(_("sh_tools_server_cmd"));
206 
207   if ((srvcmd == NULL) || (srvcmd[0] == '\0') || (sl_strlen(srvcmd) < 4))
208     {
209       SL_RET0(_("sh_socket_server_cmd"));
210     }
211 
212   if (0 == strncmp(srvcmd, _("STOP"), 4))
213     {
214       TPT((0, FIL__, __LINE__, _("msg=<stop command from server>\n")));
215 #ifdef SIGQUIT
216       raise(SIGQUIT);
217 #else
218       sig_terminate       = 1;
219       ++sig_raised;
220 #endif
221     }
222 
223   else if (0 == strncmp(srvcmd, _("RELOAD"), 6))
224     {
225       TPT((0, FIL__, __LINE__, _("msg=<reload command from server>\n")));
226 #ifdef SIGHUP
227       raise(SIGHUP);
228 #else
229       sig_config_read_again = 1;
230       ++sig_raised;
231 #endif
232     }
233 
234   else if (0 == strncmp(srvcmd, _("DELTA:"), 6))
235     {
236       TPT((0, FIL__, __LINE__, _("msg=<delta load command from server>\n")));
237 
238       if (sh_socket_store_uuid(srvcmd) == 0)
239 	{
240 	  ++sh_load_delta_flag;
241 	  ++sig_raised;
242 	}
243     }
244 
245   else if (0 == strncmp(srvcmd, _("SCAN"), 4))
246     {
247       TPT((0, FIL__, __LINE__, _("msg=<scan command from server>\n")));
248       if (sh.flag.isdaemon == S_TRUE)
249 	{
250 #ifdef SIGTTOU
251 	  raise(SIGTTOU);
252 #else
253 	  sig_force_check = 1;
254 	  ++sig_raised;
255 #endif
256 	}
257       else
258 	{
259 	  sig_force_check = 1;
260 	  ++sig_raised;
261 	}
262     }
263 
264   /* Unknown command
265    */
266   else
267     {
268       sh_error_handle(SH_ERR_WARN, FIL__, __LINE__, 0, MSG_E_SUBGEN,
269 		      srvcmd,
270 		      _("sh_socket_server_cmd"));
271     }
272   SL_RET0(_("sh_socket_server_cmd"));
273 }
274 #endif  /* #if defined (SH_WITH_CLIENT) */
275 
276 #if defined(SH_WITH_SERVER)
277 #include <errno.h>
278 
279 #include <sys/types.h>
280 #include <sys/stat.h>
281 
282 #include <unistd.h>
283 #include <fcntl.h>
284 
285 #include <time.h>
286 
287 #include <sys/socket.h>
288 #include <sys/un.h>
289 
290 
291 #ifdef HAVE_SYS_UIO_H
292 #include <sys/uio.h>
293 #endif
294 #if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED)
295 #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
296 #include <sys/param.h>
297 #include <sys/ucred.h>
298 #endif
299 #endif
300 
301 
302 int    pf_unix_fd  = -1;
303 static char * sh_sockname = NULL;
304 static char   sh_sockpass_real[SOCKPASS_MAX+1];
305 
306 struct socket_cmd {
307   char cmd[SH_MAXMSGLEN];
308   char clt[SH_MAXMSGLEN];
309   char cti[81];
310   struct socket_cmd * next;
311 };
312 
313 #if !defined(O_NONBLOCK)
314 #if defined(O_NDELAY)
315 #define O_NONBLOCK  O_NDELAY
316 #else
317 #define O_NONBLOCK  0
318 #endif
319 #endif
320 
321 #if !defined(AF_FILE)
322 #define AF_FILE AF_UNIX
323 #endif
324 
325 static struct socket_cmd * cmdlist    = NULL;
326 static struct socket_cmd * runlist    = NULL;
327 
328 static int    sh_socket_flaguse = S_FALSE;
329 static int    sh_socket_flaguid = 0;
330 
331 #include "sh_utils.h"
332 
333 /* The reload list stores information about
334  * reloads confirmed by clients (startup and/or
335  * runtime cinfiguration reloaded).
336  */
337 struct reload_cmd {
338   char          clt[SH_MAXMSGLEN];
339   time_t        cti;
340   struct reload_cmd * next;
341 };
342 static struct reload_cmd * reloadlist = NULL;
343 
sh_socket_add2reload(const char * clt)344 void sh_socket_add2reload (const char * clt)
345 {
346   struct reload_cmd  * new = reloadlist;
347 
348   while (new)
349     {
350       if (0 == sl_strcmp(new->clt, clt))
351 	{
352 #ifdef SH_DEBUG_SOCKET
353 	  fprintf(stderr, "add2reload: time reset for %s\n", clt);
354 #endif
355 	  sl_strlcpy (new->clt, clt, SH_MAXMSGLEN);
356 	  new->cti = time(NULL);
357 	  return;
358 	}
359       new = new->next;
360     }
361 
362   new = SH_ALLOC(sizeof(struct reload_cmd));
363 #ifdef SH_DEBUG_SOCKET
364   fprintf(stderr, "add2reload: time set for %s\n", clt);
365 #endif
366   sl_strlcpy (new->clt, clt, SH_MAXMSGLEN);
367   new->cti = time(NULL);
368 
369   new->next    = reloadlist;
370   reloadlist   = new;
371 
372   return;
373 }
374 
375 #include "zAVLTree.h"
376 #include "sh_html.h"
377 #include "sh_tools.h"
378 static void sh_socket_add2list (struct socket_cmd * in);
379 
sh_socket_probe4reload(void)380 static void sh_socket_probe4reload (void)
381 {
382   struct reload_cmd  * new;
383   struct socket_cmd    cmd;
384 
385   zAVLCursor avlcursor;
386   client_t * item;
387   extern zAVLTree * all_clients;
388 
389   char     * file;
390   unsigned long dummy;
391   struct stat buf;
392 
393   for (item = (client_t *) zAVLFirst(&avlcursor, all_clients); item;
394        item = (client_t *) zAVLNext(&avlcursor))
395     {
396       if (item->status_now != CLT_INACTIVE)
397 	{
398 	  int flag = 0;
399 
400 	  file = get_client_conf_file (item->hostname, &dummy);
401 
402 	  if (0 == stat (file, &buf))
403 	    {
404 	      new = reloadlist;
405 	      while (new)
406 		{
407 		  if (0 == sl_strcmp(new->clt, item->hostname))
408 		    {
409 		      flag = 1; /* Client is in list already */
410 
411 		      if (buf.st_mtime > new->cti)
412 			{
413 			  /* reload */
414 			  sl_strlcpy(cmd.cmd, _("RELOAD"),    SH_MAXMSGLEN);
415 			  sl_strlcpy(cmd.clt, item->hostname, SH_MAXMSGLEN);
416 			  sh_socket_add2list (&cmd);
417 			}
418 		      break;
419 		    }
420 		  new = new->next;
421 		}
422 
423 	      if (flag == 0)
424 		{
425 		  /* client is active, but start message has been missed; reload
426 		   */
427 		  sl_strlcpy(cmd.cmd, _("RELOAD"),    SH_MAXMSGLEN);
428 		  sl_strlcpy(cmd.clt, item->hostname, SH_MAXMSGLEN);
429 		  sh_socket_add2list (&cmd);
430 
431 		  /* Add the client to the reload list and set
432 		   * time to 0, since we don't know the startup time.
433 		   */
434 		  sh_socket_add2reload (item->hostname);
435 		  new = reloadlist;
436 		  while (new)
437 		    {
438 		      if (0 == sl_strcmp(new->clt, item->hostname))
439 			{
440 			  new->cti = 0;
441 			  break;
442 			}
443 		      new = new->next;
444 		    }
445 		}
446 	    } /* if stat(file).. */
447 	} /* if !CLT_INACTIVE */
448     } /* loop over clients */
449   return;
450 }
451 
sh_get_sockpass(void)452 char * sh_get_sockpass (void)
453 {
454   size_t j = 0;
455 
456   while (skey->sh_sockpass[2*j] != '\0' && j < sizeof(sh_sockpass_real))
457     {
458       sh_sockpass_real[j] = skey->sh_sockpass[2*j];
459       ++j;
460     }
461   sh_sockpass_real[j] = '\0';
462 
463   return sh_sockpass_real;
464 }
465 
sh_set_sockpass(void)466 void sh_set_sockpass (void)
467 {
468   int j;
469   for (j = 0; j < 15; ++j)
470     {
471       sh_sockpass_real[j] = '\0';
472     }
473 }
474 
sh_socket_use(const char * c)475 int sh_socket_use (const char * c)
476 {
477   return sh_util_flagval(c, &sh_socket_flaguse);
478 }
479 
sh_socket_remove()480 int sh_socket_remove ()
481 {
482   int retval = 0;
483 #ifdef S_ISSOCK
484   struct stat sbuf;
485 #endif
486 
487   SL_ENTER(_("sh_socket_remove"));
488 
489   if (NULL == sh_sockname)
490     {
491       SL_RETURN((retval),_("sh_socket_remove"));
492     }
493 
494   if (0 != tf_trust_check (DEFAULT_PIDDIR, SL_YESPRIV))
495     {
496       SL_RETURN((-1),_("sh_socket_remove"));
497     }
498 
499   if ( (retry_lstat(FIL__, __LINE__, sh_sockname, &sbuf) == 0) &&
500        (sbuf.st_uid == getuid()))
501     {
502 #ifdef S_ISSOCK
503       if (S_ISSOCK (sbuf.st_mode))
504 	{
505 	  retval = retry_aud_unlink (FIL__, __LINE__, sh_sockname);
506 	}
507 #else
508       retval = retry_aud_unlink (FIL__, __LINE__, sh_sockname);
509 #endif
510     }
511   SL_RETURN((retval),_("sh_socket_remove"));
512 }
513 
514 #if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && !defined(HAVE_STRUCT_CMSGCRED) && !defined(HAVE_STRUCT_FCRED) && !(defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
515 
516 #define NEED_PASSWORD_AUTH
517 #endif
518 
sh_socket_uid(const char * c)519 int sh_socket_uid (const char * c)
520 {
521   uid_t val = (uid_t) strtol (c, (char **)NULL, 10);
522   sh_socket_flaguid = val;
523 #if defined(NEED_PASSWORD_AUTH)
524   sh_error_handle(SH_ERR_WARN, FIL__, __LINE__, errno, MSG_E_SUBGEN,
525 		  _("Config option SetSocketAllowUID not supported, use SetSocketPassword"),
526 		  _("sh_socket_uid"));
527 #endif
528   return 0;
529 }
530 
sh_socket_password(const char * c)531 int sh_socket_password (const char * c)
532 {
533 #if defined(NEED_PASSWORD_AUTH)
534   int j, i;
535 
536 #define LCG(n) ((69069 * n) & 0xffffffffUL)
537 
538   i = sl_strlen(c);
539   if (i > SOCKPASS_MAX) {
540     return -1;
541   }
542   for (j = 0; j < (2*SOCKPASS_MAX+1); ++j)
543     {
544       skey->sh_sockpass[j] = '\0';
545     }
546   for (j = 0; j < i; ++j)
547     {
548       skey->sh_sockpass[2*j]     = c[j];
549       skey->sh_sockpass[(2*j)+1] = (LCG(c[j]) % 256);
550     }
551   return 0;
552 #else
553   sh_error_handle(SH_ERR_WARN, FIL__, __LINE__, errno, MSG_E_SUBGEN,
554 		  _("Config option SetSocketPassword not supported, use SetSocketAllowUID"),
555 		  _("sh_socket_password"));
556   (void) c;
557   return 0;
558 #endif
559 }
560 
561 
sh_socket_open_int()562 int sh_socket_open_int ()
563 {
564   struct sockaddr_un name;
565   size_t size;
566   int    flags;
567 #if defined(SO_PASSCRED)
568   socklen_t    optval = 1;
569 #endif
570   struct stat buf;
571   char errbuf[SH_ERRBUF_SIZE];
572 
573   SL_ENTER(_("sh_socket_open_int"));
574 
575   if (sh_socket_flaguse == S_FALSE)
576     {
577       SL_RETURN(0, _("sh_socket_open_int"));
578     }
579 
580   if (sh_sockname == NULL)
581     {
582       size = sl_strlen(DEFAULT_PIDDIR) + 1 + sl_strlen(SH_INSTALL_NAME) + 6;
583       sh_sockname = SH_ALLOC(size); /* compile-time constant */
584       sl_strlcpy(sh_sockname, DEFAULT_PIDDIR, size);
585       sl_strlcat(sh_sockname, "/", size);
586       sl_strlcat(sh_sockname, SH_INSTALL_NAME, size);
587       sl_strlcat(sh_sockname, _(".sock"), size);
588     }
589 
590   if (0 != sh_unix_check_piddir (sh_sockname))
591     {
592       SH_FREE(sh_sockname);
593       SL_RETURN((-1),_("sh_socket_open_int"));
594     }
595 
596   pf_unix_fd = socket (PF_UNIX, SOCK_STREAM, 0);
597   if ((pf_unix_fd) < 0)
598     {
599       sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
600 		       sh_error_message (errno, errbuf, sizeof(errbuf)),
601 		       _("sh_socket_open_int: socket"));
602       SL_RETURN( (-1), _("sh_socket_open_int"));
603     }
604 
605   if (sizeof(name.sun_path) < (1 + sl_strlen(sh_sockname)))
606     {
607       sl_close_fd(FIL__, __LINE__, pf_unix_fd); pf_unix_fd = -1;
608       sh_error_handle ((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
609 		       _("PID dir path too long"),
610 		       _("sh_socket_open_int"));
611       SL_RETURN( (-1), _("sh_socket_open_int"));
612     }
613 
614   name.sun_family = AF_FILE;
615   sl_strlcpy (name.sun_path, sh_sockname, sizeof(name.sun_path));
616 
617   size = (offsetof (struct sockaddr_un, sun_path)
618           + strlen (name.sun_path) + 1);
619 
620   flags = retry_lstat (FIL__, __LINE__, sh_sockname, &buf);
621 
622   if (flags == 0)
623     {
624       sh_error_handle(SH_ERR_INFO, FIL__, __LINE__, -1, MSG_E_SUBGEN,
625 		      _("Socket exists, trying to unlink it"),
626 		      _("sh_socket_open_int"));
627       if (sh_socket_remove() < 0)
628 	{
629 	  sl_close_fd(FIL__, __LINE__, pf_unix_fd); pf_unix_fd = -1;
630 	  sh_error_handle ((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
631 			   _("Unlink of socket failed, maybe path not trusted"),
632 			   _("sh_socket_open_int"));
633 	  SL_RETURN( (-1), _("sh_socket_open_int"));
634 	}
635     }
636 
637   if (bind ((pf_unix_fd), (struct sockaddr *) &name, size) < 0)
638     {
639       sl_close_fd(FIL__, __LINE__, pf_unix_fd); pf_unix_fd = -1;
640       sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
641 		       sh_error_message (errno, errbuf, sizeof(errbuf)),
642 		       _("sh_socket_open_int: bind"));
643       SL_RETURN( (-1), _("sh_socket_open_int"));
644     }
645 
646 #ifdef SO_PASSCRED
647   if (0 != setsockopt(pf_unix_fd, SOL_SOCKET, SO_PASSCRED,
648 		      &optval, sizeof(optval)))
649     {
650       sl_close_fd(FIL__, __LINE__, pf_unix_fd); pf_unix_fd = -1;
651       sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
652 		       sh_error_message (errno, errbuf, sizeof(errbuf)),
653 		       _("sh_socket_open_int: setsockopt"));
654       SL_RETURN( (-1), _("sh_socket_open_int"));
655     }
656 #endif
657 
658   flags = fcntl((pf_unix_fd), F_GETFL);
659   if (flags < 0)
660     {
661       sl_close_fd(FIL__, __LINE__, pf_unix_fd); pf_unix_fd = -1;
662       sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
663 		       sh_error_message (errno, errbuf, sizeof(errbuf)),
664 		       _("sh_socket_open_int: fcntl1"));
665       SL_RETURN( (-1), _("sh_socket_open_int"));
666     }
667 
668   flags = fcntl((pf_unix_fd), F_SETFL, flags|O_NONBLOCK);
669   if (flags < 0)
670     {
671       sl_close_fd(FIL__, __LINE__, pf_unix_fd); pf_unix_fd = -1;
672       sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
673 		      sh_error_message (errno, errbuf, sizeof(errbuf)),
674 		      _("sh_socket_open_int: fcntl2"));
675       SL_RETURN( (-1), _("sh_socket_open_int"));
676     }
677 
678   if (0 != listen(pf_unix_fd, 5))
679     {
680       sl_close_fd(FIL__, __LINE__, pf_unix_fd); pf_unix_fd = -1;
681       sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
682 		       sh_error_message (errno, errbuf, sizeof(errbuf)),
683 		       _("sh_socket_open_int: listen"));
684       SL_RETURN( (-1), _("sh_socket_open_int"));
685     }
686   SL_RETURN( (0), _("sh_socket_open_int"));
687 }
688 
689 
690 /*
691  * Parts of the socket authentication code is copied from PostgreSQL:
692  *
693  * PostgreSQL Database Management System
694  * (formerly known as Postgres, then as Postgres95)
695  *
696  * Portions Copyright (c) 1996-2001, The PostgreSQL Global Development Group
697  *
698  * Portions Copyright (c) 1994, The Regents of the University of California
699  *
700  * Permission to use, copy, modify, and distribute this software and its
701  * documentation for any purpose, without fee, and without a written agreement
702  * is hereby granted, provided that the above copyright notice and this
703  * paragraph and the following two paragraphs appear in all copies.
704  *
705  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
706  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
707  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
708  * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
709  * POSSIBILITY OF SUCH DAMAGE.
710  *
711  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
712  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
713  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
714  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
715  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
716  */
717 
receive_message(int talkfd,struct msghdr * msg,size_t message_size)718 static int receive_message(int talkfd, struct msghdr * msg, size_t message_size)
719 {
720   unsigned int retry = 0;
721   int nbytes;
722   char * message = msg->msg_iov->iov_base;
723   char errbuf[SH_ERRBUF_SIZE];
724 
725   do {
726     nbytes = recvmsg (talkfd, msg, 0);
727     if ((nbytes < 0) && (errno != EAGAIN))
728       {
729 	sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
730 			sh_error_message (errno, errbuf, sizeof(errbuf)),
731 			_("sh_socket_read: recvmsg"));
732 	sl_close_fd(FIL__, __LINE__, talkfd);
733 	return -1;
734       }
735     else if (nbytes < 0)
736       {
737 	++retry;
738 	retry_msleep(0, 10);
739       }
740   } while ((nbytes < 0) && (retry < 3));
741 
742   /* msg.msg_iov.iov_base, filled by recvmsg
743    */
744   message[message_size-1] = '\0';
745 
746   if (nbytes < 0)
747     {
748       if (errno == EAGAIN)
749 	{
750 	  /* no data */
751 	  sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, errno, MSG_E_SUBGEN,
752 			  sh_error_message (errno, errbuf, sizeof(errbuf)),
753 			  _("sh_socket_read: recvfrom"));
754 	  sl_close_fd(FIL__, __LINE__, talkfd);
755 	  return 0;
756 	}
757       sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
758 		      sh_error_message (errno, errbuf, sizeof(errbuf)),
759 		      _("sh_socket_read: recvfrom"));
760       sl_close_fd(FIL__, __LINE__, talkfd);
761       return -1;
762     }
763   return 0;
764 }
765 
766 #if defined(HAVE_GETPEEREID)
767 
get_peer_uid(int talkfd)768 static int get_peer_uid(int talkfd)
769 {
770   uid_t peer_uid;
771   gid_t peer_gid;
772   char errbuf[SH_ERRBUF_SIZE];
773 
774   if (0 != getpeereid(talkfd, &peer_uid, &peer_gid))
775     {
776       sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
777 		      sh_error_message (errno, errbuf, sizeof(errbuf)),
778 		      _("sh_socket_read: getpeereid"));
779       sl_close_fd(FIL__, __LINE__, talkfd);
780       return -1;
781     }
782   return peer_uid;
783 }
784 
785 #elif defined(SO_PEERCRED)
786 
get_peer_uid(int talkfd)787 static int get_peer_uid(int talkfd)
788 {
789   char errbuf[SH_ERRBUF_SIZE];
790   struct ucred cr;
791 #ifdef HAVE_SOCKLEN_T
792   socklen_t cl = sizeof(cr);
793 #else
794   int       cl = sizeof(cr);
795 #endif
796 
797   if (0 != getsockopt(talkfd, SOL_SOCKET, SO_PEERCRED, &cr, &cl))
798     {
799       sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
800 		      sh_error_message (errno, errbuf, sizeof(errbuf)),
801 		      _("sh_socket_read: getsockopt"));
802       sl_close_fd(FIL__, __LINE__, talkfd);
803       return -1;
804     }
805   return cr.uid;
806 }
807 
808 #endif
809 
810 #if defined(NEED_PASSWORD_AUTH)
check_password(char * message,size_t msglen,int * client_uid,int talkfd)811 char * check_password(char * message, size_t msglen, int * client_uid, int talkfd)
812 {
813   char * cmd = NULL;
814   char * eopw = NULL;
815   char * goodpassword = NULL;
816 
817   goodpassword = sh_get_sockpass();
818   eopw = strchr(message, '@');
819   if (eopw)
820     *eopw = '\0';
821   /*
822    * message is null-terminated and >> goodpassword
823    */
824   if (0 == strcmp(goodpassword, message) &&
825       strlen(goodpassword) < (msglen/2))
826     {
827       *client_uid = sh_socket_flaguid;
828       cmd = &message[strlen(goodpassword)+1];
829       sh_set_sockpass();
830     }
831   else
832     {
833       sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
834 		      _("Bad password"),
835 		      _("sh_socket_read"));
836       sh_set_sockpass();
837       sl_close_fd(FIL__, __LINE__, talkfd);
838       return NULL;
839     }
840   return cmd;
841 }
842 #endif
843 
844 static int list_all (int talkfd, char * cmd);
845 static int process_message(int talkfd, char * cmd, struct socket_cmd * srvcmd);
846 
847 static
sh_socket_read(struct socket_cmd * srvcmd)848 int sh_socket_read (struct socket_cmd * srvcmd)
849 {
850   char message[SH_MAXMSG];
851   struct sockaddr_un name;
852   ACCEPT_TYPE_ARG3 size = sizeof(name);
853   int talkfd;
854   char * cmd = NULL;
855   int  client_uid = -1;
856   char errbuf[SH_ERRBUF_SIZE];
857   struct msghdr msg;
858   struct iovec iov;
859   int status;
860 
861   if (pf_unix_fd  < 0)
862     return 0;
863 
864   iov.iov_base = (char *) &message;
865   iov.iov_len  = sizeof(message);
866 
867   memset (&msg, 0, sizeof (msg));
868   msg.msg_iov = &iov;
869   msg.msg_iovlen = 1;
870 
871   /* the socket is non-blocking
872    * 'name' is the address of the sender socket
873    */
874   do {
875     talkfd = accept(pf_unix_fd, (struct sockaddr *) &name, &size);
876   } while (talkfd < 0 && errno == EINTR);
877 
878   if ((talkfd < 0) && (errno == EAGAIN))
879     {
880       return 0;
881     }
882   else if (talkfd < 0)
883     {
884       sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
885 		      sh_error_message (errno, errbuf, sizeof(errbuf)),
886 		      _("sh_socket_read: accept"));
887       return -1;
888     }
889 
890   if (receive_message(talkfd, &msg, sizeof(message)) < 0)
891     return -1;
892 
893   /* Authenticate request by peer uid or password.
894    */
895 #if defined(HAVE_GETPEEREID)
896   client_uid = get_peer_uid(talkfd);
897   cmd = message;
898 
899 #elif defined(SO_PEERCRED)
900   client_uid = get_peer_uid(talkfd);
901   cmd = message;
902 
903 #elif defined(NEED_PASSWORD_AUTH)
904   cmd = check_password(message, sizeof(message), &client_uid, talkfd);
905 
906 #else
907   sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
908 		  _("Socket credentials not supported on this OS"),
909 		  _("sh_socket_read"));
910   sl_close_fd(FIL__, __LINE__, talkfd);
911   return -1;
912 #endif
913 
914   if (client_uid != sh_socket_flaguid)
915     {
916       sh_error_handle((-1), FIL__, __LINE__, client_uid, MSG_E_SUBGEN,
917 		      _("client does not have required uid"),
918 		      _("sh_socket_read: getsockopt"));
919       sl_close_fd(FIL__, __LINE__, talkfd);
920       return -1;
921     }
922 
923   status = process_message(talkfd, cmd, srvcmd);
924 
925   sl_close_fd(FIL__, __LINE__, talkfd);
926   return status;
927 }
928 
check_valid_command(const char * str)929 static int check_valid_command(const char * str)
930 {
931   unsigned int i = 0;
932   char * commands[] = { N_("DELTA"),  N_("RELOAD"),  N_("STOP"), N_("SCAN"),
933 			N_("CANCEL"), N_("LISTALL"), N_("LIST"), N_("PROBE"), NULL };
934 
935   while (commands[i])
936     {
937       if (0 == strcmp(_(commands[i]), str))
938 	{
939 	  return 0;
940 	}
941       ++i;
942     }
943   return -1;
944 }
945 
send_reply(int fd,char * msg)946 static int send_reply (int fd, char * msg)
947 {
948   int nbytes = send (fd, msg, strlen(msg) + 1, 0);
949 
950   if (nbytes < 0)
951     {
952       char errbuf[SH_ERRBUF_SIZE];
953       sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
954 		      sh_error_message (errno, errbuf, sizeof(errbuf)),
955 		      _("send_reply"));
956       return -1;
957     }
958 
959   return nbytes;
960 }
961 
process_message(int talkfd,char * cmd,struct socket_cmd * srvcmd)962 static int process_message(int talkfd, char * cmd, struct socket_cmd * srvcmd)
963 {
964   int nbytes;
965   char error_type[SH_ERRBUF_SIZE] = { '\0' };
966   char * clt  = (cmd) ? strchr(cmd, ':') : NULL;
967 
968   if (clt && 0 == strncmp(cmd, _("DELTA:"), 6))
969     {
970       /* DELTA:uuid:hostname
971        */
972       char * uuid = clt;
973 
974       *uuid = '\0'; ++uuid;
975       clt = strchr(uuid, ':');
976       if (clt) { *clt = '\0'; ++clt; }
977 
978       if (sh_uuid_check(uuid) < 0)
979 	{
980 	  sl_strlcpy(error_type, _("!E:uuid-format:"), sizeof(error_type));
981 	  sl_strlcat(error_type, uuid, sizeof(error_type));
982 	  clt = NULL;
983 	}
984 
985       --uuid; *uuid = ':';
986     }
987   else if (clt && *clt == ':')
988     {
989       *clt = '\0'; ++clt;
990       if (check_valid_command(cmd) < 0)
991 	{
992 	  sl_strlcpy(error_type, _("!E:cmd-invalid:"), sizeof(error_type));
993 	  sl_strlcat(error_type, cmd, sizeof(error_type));
994 	  clt = NULL;
995 	}
996     }
997 
998   if (clt != NULL)
999     {
1000       if (sl_strlen(cmd) >= SH_MAXMSGLEN)
1001 	{
1002 	  sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
1003 			  _("Bad message format: command too long"),
1004 			  _("sh_socket_read"));
1005 	  sl_strlcpy(error_type, _("!E:cmd-toolong"), sizeof(error_type));
1006 	  send_reply(talkfd, error_type);
1007 	  return -1;
1008 	}
1009       else if (sl_strlen(clt) >= SH_MAXMSGLEN)
1010 	{
1011 	  sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
1012 			  _("Bad message format: hostname too long"),
1013 			  _("sh_socket_read"));
1014 	  sl_strlcpy(error_type, _("!E:hostname-toolong"), sizeof(error_type));
1015 	  send_reply(talkfd, error_type);
1016 	  return -1;
1017 	}
1018 
1019       if (0 == strncmp(cmd, _("LIST"), 4))
1020 	return list_all(talkfd, cmd);
1021       else if (0 == strncmp(cmd, _("PROBE"), 4))
1022 	{
1023 	  sh_socket_probe4reload();
1024 	  sl_strlcpy(cmd, _("LIST"), 5);
1025 	  return list_all(talkfd, cmd);
1026 	}
1027 
1028       sl_strlcpy (srvcmd->cmd, cmd, SH_MAXMSGLEN);
1029       sl_strlcpy (srvcmd->clt, clt, SH_MAXMSGLEN);
1030       --clt; *clt = ':';
1031     }
1032   else
1033     {
1034       sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
1035 		      _("Bad message format"),
1036 		      _("sh_socket_read"));
1037       if (error_type[0] == '\0')
1038 	sl_strlcpy(error_type, _("!E:message-format"), sizeof(error_type));
1039       send_reply(talkfd, error_type);
1040       return -1;
1041     }
1042 
1043   /* Bounce the message back to the sender.
1044    */
1045   nbytes = send_reply(talkfd, cmd);
1046 
1047   return nbytes;
1048 }
1049 
list_all(int talkfd,char * cmd)1050 static int list_all (int talkfd, char * cmd)
1051 {
1052   int nbytes;
1053   struct socket_cmd * list_cmd;
1054   char message[SH_MAXMSG];
1055   char errbuf[SH_ERRBUF_SIZE];
1056 
1057   if (0 == strncmp(cmd, _("LISTALL"), 7))
1058     {
1059       list_cmd = runlist;
1060       while (list_cmd)
1061 	{
1062 	  sl_snprintf(message, sizeof(message), _("SENT  %42s  %32s  %s"),
1063 		      list_cmd->cmd, list_cmd->clt, list_cmd->cti);
1064 
1065 	  nbytes = send (talkfd, message, sl_strlen(message) + 1, 0);
1066 	  if (nbytes < 0)
1067 	    {
1068 	      sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
1069 			      sh_error_message (errno, errbuf, sizeof(errbuf)),
1070 			      _("sh_socket_read: sendto"));
1071 	      return -1;
1072 	    }
1073 	  list_cmd = list_cmd->next;
1074 	}
1075     }
1076 
1077   list_cmd = cmdlist;
1078   while (list_cmd)
1079     {
1080       sl_snprintf(message, sizeof(message), _(">>>>  %42s  %32s  %s"),
1081 		  list_cmd->cmd, list_cmd->clt, list_cmd->cti);
1082 
1083       nbytes = send (talkfd, message, sl_strlen(message) + 1, 0);
1084       if (nbytes < 0)
1085 	{
1086 	  sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
1087 			  sh_error_message (errno, errbuf, sizeof(errbuf)),
1088 			  _("sh_socket_read: sendto"));
1089 	  return -1;
1090 	}
1091       list_cmd = list_cmd->next;
1092     }
1093 
1094   send (talkfd, _("END"), 4, 0);
1095   return 0;
1096 }
1097 
sh_socket_add2list(struct socket_cmd * in)1098 static void sh_socket_add2list (struct socket_cmd * in)
1099 {
1100   struct socket_cmd  * new  = cmdlist;
1101   struct socket_cmd  * last = cmdlist;
1102 
1103   while (new)
1104     {
1105       /* Only skip identical commands.
1106        */
1107       if (0 == sl_strcmp(new->clt,  in->clt) &&
1108 	  0 == sl_strcmp(new->cmd,  in->cmd))
1109 	{
1110 	  (void) sh_unix_time(0, new->cti, sizeof(new->cti));
1111 	  return;
1112 	}
1113       new = new->next;
1114     }
1115 
1116   new = SH_ALLOC(sizeof(struct socket_cmd));
1117   sl_strlcpy (new->cmd,  in->cmd,  sizeof(new->cmd));
1118   sl_strlcpy (new->clt,  in->clt,  sizeof(new->clt));
1119   (void) sh_unix_time(0, new->cti, sizeof(new->cti));
1120   new->next  = NULL;
1121 
1122   if (last)
1123     {
1124       while (last->next) { last = last->next; }
1125       last->next = new;
1126     }
1127   else
1128     {
1129       cmdlist = new;
1130     }
1131   return;
1132 }
1133 
sh_socket_add2run(struct socket_cmd * in)1134 static void sh_socket_add2run (struct socket_cmd * in)
1135 {
1136   struct socket_cmd  * new  = runlist;
1137   struct socket_cmd  * last = runlist;
1138 
1139   while (new)
1140     {
1141       /* Only skip identical commands. First 5 will
1142        * make all 'DELTA' identical.
1143        */
1144       if (0 == sl_strcmp(new->clt,  in->clt) &&
1145 	  0 == sl_strncmp(new->cmd,  in->cmd, 5))
1146 	{
1147 	  sl_strlcpy (new->cmd,  in->cmd,  sizeof(new->cmd));
1148 	  (void) sh_unix_time(0, new->cti, sizeof(new->cti));
1149 	  return;
1150 	}
1151       new = new->next;
1152     }
1153 
1154   new = SH_ALLOC(sizeof(struct socket_cmd));
1155   sl_strlcpy (new->cmd,  in->cmd,  sizeof(new->cmd));
1156   sl_strlcpy (new->clt,  in->clt,  sizeof(new->clt));
1157 #ifdef SH_DEBUG_SOCKET
1158   fprintf(stderr, "add2run: time set for %s\n", new->clt);
1159 #endif
1160   (void) sh_unix_time(0, new->cti, sizeof(new->cti));
1161   new->next  = NULL;
1162 
1163   if (last)
1164     {
1165       while (last->next) { last = last->next; }
1166       last->next = new;
1167     }
1168   else
1169     {
1170       runlist = new;
1171     }
1172   return;
1173 }
1174 
1175 
1176 
sh_socket_rm2list(const char * client_name,int remove_all)1177 static void sh_socket_rm2list (const char * client_name, int remove_all)
1178 {
1179   struct socket_cmd * old = cmdlist;
1180   struct socket_cmd * new = cmdlist;
1181 
1182   while (new)
1183     {
1184       if (0 == sl_strcmp(new->clt, client_name))
1185 	{
1186 	  if ((new == cmdlist) && (new->next == NULL))
1187 	    {
1188 	      /* There is only one entry */
1189 	      cmdlist = NULL;
1190 	      SH_FREE(new);
1191 	      return;
1192 	    }
1193 	  else if (new == cmdlist)
1194 	    {
1195 	      /* first entry: new = old = cmdlist */
1196 	      cmdlist = new->next;
1197 	      SH_FREE(new);
1198 	      if (remove_all == S_FALSE)
1199 		return;
1200 	      old = cmdlist;
1201 	      new = cmdlist;
1202 	      continue;
1203 	    }
1204 	  else
1205 	    {
1206 	      old->next = new->next;
1207 	      SH_FREE(new);
1208 	      if (remove_all == S_FALSE)
1209 		return;
1210 	      new = old;
1211 	    }
1212 	}
1213       old = new;
1214       new = new->next;
1215     }
1216   return;
1217 }
1218 
1219 /* poll the socket to gather input
1220  */
sh_socket_poll()1221 int sh_socket_poll()
1222 {
1223   struct socket_cmd   cmd;
1224   char   cancel_cmd[SH_MAXMSGLEN];
1225 
1226   /* struct pollfd sh_poll = { pf_unix_fd, POLLIN, 0 }; */
1227 
1228   if (pf_unix_fd  < 0)
1229     return 0;
1230 
1231   sl_strlcpy(cancel_cmd, _("CANCEL"), sizeof(cancel_cmd));
1232 
1233   while (sh_socket_read (&cmd) > 0)
1234     {
1235       if (0 == sl_strcmp(cmd.cmd, cancel_cmd))
1236 	sh_socket_rm2list  (cmd.clt, S_TRUE);
1237       else
1238 	sh_socket_add2list (&cmd);
1239     }
1240   return 0;
1241 }
1242 
1243 /* return the command associated with client_name
1244    and remove the corresponding entry
1245  */
sh_socket_check(const char * client_name)1246 char * sh_socket_check(const char * client_name)
1247 {
1248   struct socket_cmd * new = cmdlist;
1249   static char         out[SH_MAXMSGLEN];
1250 
1251   while (new)
1252     {
1253       if (0 == sl_strcmp(new->clt, client_name))
1254 	{
1255 	  sl_strlcpy(out,  new->cmd,  sizeof(out));
1256 	  sh_socket_add2run (new);
1257 	  sh_socket_rm2list (client_name, S_FALSE);
1258 	  return out;
1259 	}
1260       new = new->next;
1261     }
1262   return NULL;
1263 }
1264 /* #if defined (SH_WITH_SERVER)
1265  */
1266 #endif
1267 
1268 
1269 #ifdef SH_CUTEST
1270 #include "CuTest.h"
1271 
Test_cmdlist(CuTest * tc)1272 void Test_cmdlist (CuTest *tc) {
1273 
1274 #if defined (SH_WITH_SERVER)
1275   struct socket_cmd cmd;
1276   char * p;
1277 
1278   sl_strlcpy(cmd.clt, "one", sizeof(cmd.clt));
1279   sl_strlcpy(cmd.cmd, "RELOAD", sizeof(cmd.cmd));
1280 
1281   sh_socket_add2list (&cmd);
1282   p = sh_socket_check("one");
1283   CuAssertPtrNotNull(tc, p);
1284   CuAssertStrEquals(tc, "RELOAD", p);
1285 
1286   p = sh_socket_check("one");
1287   CuAssertPtrEquals(tc, NULL, p);
1288 
1289   sh_socket_add2list (&cmd);
1290   sl_strlcpy(cmd.cmd, "STOP",   sizeof(cmd.cmd));
1291   sh_socket_add2list (&cmd);
1292 
1293   sl_strlcpy(cmd.clt, "two", sizeof(cmd.clt));
1294   sl_strlcpy(cmd.cmd, "STOP", sizeof(cmd.cmd));
1295   sh_socket_add2list (&cmd);
1296   sl_strlcpy(cmd.clt, "three", sizeof(cmd.clt));
1297   sl_strlcpy(cmd.cmd, "STOP", sizeof(cmd.cmd));
1298   sh_socket_add2list (&cmd);
1299 
1300   sl_strlcpy(cmd.clt, "one", sizeof(cmd.clt));
1301   sl_strlcpy(cmd.cmd, "DELTA",   sizeof(cmd.cmd));
1302   sh_socket_add2list (&cmd);
1303 
1304   p = sh_socket_check("one");
1305   CuAssertPtrNotNull(tc, p);
1306   CuAssertStrEquals(tc, "RELOAD", p);
1307 
1308   sl_strlcpy(cmd.clt, "two", sizeof(cmd.clt));
1309   sl_strlcpy(cmd.cmd, "RELOAD", sizeof(cmd.cmd));
1310   sh_socket_add2list (&cmd);
1311   sl_strlcpy(cmd.clt, "three", sizeof(cmd.clt));
1312   sl_strlcpy(cmd.cmd, "RELOAD", sizeof(cmd.cmd));
1313   sh_socket_add2list (&cmd);
1314 
1315   p = sh_socket_check("one");
1316   CuAssertPtrNotNull(tc, p);
1317   CuAssertStrEquals(tc, "STOP", p);
1318   p = sh_socket_check("one");
1319   CuAssertPtrNotNull(tc, p);
1320   CuAssertStrEquals(tc, "DELTA", p);
1321   p = sh_socket_check("one");
1322   CuAssertPtrEquals(tc, NULL, p);
1323 
1324   /* Test removal in correct order */
1325   sl_strlcpy(cmd.clt, "one", sizeof(cmd.clt));
1326   sl_strlcpy(cmd.cmd, "RELOAD", sizeof(cmd.cmd));
1327   sh_socket_add2list (&cmd);
1328   sl_strlcpy(cmd.cmd, "STOP",   sizeof(cmd.cmd));
1329   sh_socket_add2list (&cmd);
1330   sl_strlcpy(cmd.cmd, "DELTA",  sizeof(cmd.cmd));
1331   sh_socket_add2list (&cmd);
1332   sl_strlcpy(cmd.cmd, "FOOBAR", sizeof(cmd.cmd));
1333   sh_socket_add2list (&cmd);
1334 
1335   sh_socket_rm2list ("one", S_FALSE);
1336 
1337   p = sh_socket_check("one");
1338   CuAssertPtrNotNull(tc, p);
1339   CuAssertStrEquals(tc, "STOP", p);
1340 
1341   sh_socket_rm2list ("one", S_FALSE);
1342 
1343   p = sh_socket_check("one");
1344   CuAssertPtrNotNull(tc, p);
1345   CuAssertStrEquals(tc, "FOOBAR", p);
1346 
1347   p = sh_socket_check("one");
1348   CuAssertPtrEquals(tc, NULL, p);
1349 
1350   sl_strlcpy(cmd.clt, "one", sizeof(cmd.clt));
1351   sl_strlcpy(cmd.cmd, "RELOAD", sizeof(cmd.cmd));
1352   sh_socket_add2list (&cmd);
1353   sl_strlcpy(cmd.cmd, "STOP",   sizeof(cmd.cmd));
1354   sh_socket_add2list (&cmd);
1355 
1356   sl_strlcpy(cmd.clt, "two", sizeof(cmd.clt));
1357   sl_strlcpy(cmd.cmd, "RELOAD", sizeof(cmd.cmd));
1358   sh_socket_add2list (&cmd);
1359   sl_strlcpy(cmd.clt, "three", sizeof(cmd.clt));
1360   sl_strlcpy(cmd.cmd, "RELOAD", sizeof(cmd.cmd));
1361   sh_socket_add2list (&cmd);
1362 
1363   sl_strlcpy(cmd.clt, "one", sizeof(cmd.clt));
1364   sl_strlcpy(cmd.cmd, "DELTA",  sizeof(cmd.cmd));
1365   sh_socket_add2list (&cmd);
1366   sl_strlcpy(cmd.cmd, "FOOBAR", sizeof(cmd.cmd));
1367   sh_socket_add2list (&cmd);
1368 
1369   sh_socket_rm2list ("one", S_TRUE);
1370   p = sh_socket_check("one");
1371   CuAssertPtrEquals(tc, NULL, p);
1372 
1373   p = sh_socket_check("two");
1374   CuAssertPtrNotNull(tc, p);
1375   CuAssertStrEquals(tc, "STOP", p);
1376   p = sh_socket_check("two");
1377   CuAssertPtrNotNull(tc, p);
1378   CuAssertStrEquals(tc, "RELOAD", p);
1379   p = sh_socket_check("two");
1380   CuAssertPtrEquals(tc, NULL, p);
1381 
1382   p = sh_socket_check("three");
1383   CuAssertPtrNotNull(tc, p);
1384   CuAssertStrEquals(tc, "STOP", p);
1385   p = sh_socket_check("three");
1386   CuAssertPtrNotNull(tc, p);
1387   CuAssertStrEquals(tc, "RELOAD", p);
1388   p = sh_socket_check("three");
1389   CuAssertPtrEquals(tc, NULL, p);
1390 
1391   p = sh_socket_check("four");
1392   CuAssertPtrEquals(tc, NULL, p);
1393 #else
1394   (void) tc;
1395 #endif
1396 }
1397 
1398 #endif  /* #ifdef SH_CUTEST */
1399 
1400 
1401 
1402