source: trunk/src/sh_portcheck.c@ 145

Last change on this file since 145 was 140, checked in by rainer, 17 years ago

Utility function for threaded modules.

  • Property svn:executable set to *
File size: 35.4 KB
RevLine 
[67]1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2006 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/***************************************************************************
21 *
22 * This file provides a module for samhain to check for open ports
23 * on the local machine.
24 *
25 */
26
27
28/* #define TEST_ONLY */
29#ifndef TEST_ONLY
30#include "config_xor.h"
31#endif
32
33#include <stdio.h>
34#include <string.h>
35#include <sys/types.h>
36#include <sys/socket.h>
37#include <netinet/in.h>
38#include <arpa/inet.h>
39#include <errno.h>
40#include <unistd.h>
[109]41#include <fcntl.h>
[67]42
[76]43#define PORTCHK_VERSION "1.0"
44
45#if defined(TEST_ONLY) || (defined(SH_USE_PORTCHECK) && (defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)))
46
47
[67]48#define PORTMAP
49#include <rpc/rpc.h>
50#ifdef HAVE_RPC_RPCENT_H
51#include <rpc/rpcent.h>
52#endif
53#include <rpc/pmap_clnt.h>
54#include <rpc/pmap_prot.h>
55#include <netdb.h>
56
57/*
58 * struct pmaplist {
59 * struct pmap pml_map;
60 * struct pmaplist *pml_next;
61 * };
62 */
63
64/* struct pmap {
65 * long unsigned pm_prog;
66 * long unsigned pm_vers;
67 * long unsigned pm_prot;
68 * long unsigned pm_port;
69 * };
70 */
71
72/* TIME_WAIT ? 60-240 seconds */
73
74/* the size of an interface string
75 */
76#define SH_INTERFACE_SIZE 16
77
78#define SH_PORT_NOT 0
79#define SH_PORT_REQ 1
80#define SH_PORT_OPT 2
[127]81#define SH_PORT_IGN 3
[67]82
83#define SH_PORT_MISS 0
84#define SH_PORT_ISOK 1
85#define SH_PORT_UNKN 2
86
87#define SH_PORT_NOREPT 0
88#define SH_PORT_REPORT 1
89
90struct sh_portentry {
91 int port;
92 char interface[SH_INTERFACE_SIZE];
93 char * service;
94 char * error;
95 int flag; /* required or not */
96 int status; /* missing or not */
97 struct sh_portentry * next;
98};
99
100static struct sh_portentry * portlist_tcp = NULL;
101static struct sh_portentry * portlist_udp = NULL;
102
103#define SH_PORTCHK_INTERVAL 300
104
105static int sh_portchk_check_udp = 1;
106static int sh_portchk_active = 1;
107static int sh_portchk_interval = SH_PORTCHK_INTERVAL;
108#if !defined(TEST_ONLY)
109
110#define FIL__ _("sh_portcheck.c")
111#include "samhain.h"
112#include "sh_error.h"
113#include "sh_mem.h"
[109]114#include "sh_calls.h"
[67]115#include "sh_utils.h"
116#include "sh_modules.h"
[137]117#include "sh_pthread.h"
[67]118
119static int sh_portchk_severity = SH_ERR_SEVERE;
120#endif
121
[127]122/* Exported interface to add ignoreable ports as 'iface:portlist'
123 */
124static int sh_portchk_add_ignore (const char * str);
125
[67]126/* Exported interface to add required ports as 'iface:portlist'
127 */
128static int sh_portchk_add_required (const char * str);
129
130/* Exported interface to add optional ports as 'iface:portlist'
131 */
132static int sh_portchk_add_optional (const char * str);
133
134/* Exported interface to add an ethernet interface
135 */
136static int sh_portchk_add_interface (const char * str);
137
138
139#ifndef TEST_ONLY
140
141static int sh_portchk_set_interval (const char * c)
142{
143 int retval = 0;
144 long val;
145
146 SL_ENTER(_("sh_portchk_set_interval"));
147 val = strtol (c, (char **)NULL, 10);
148 if (val <= 0)
149 {
150 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
151 _("port check interval"), c);
152 retval = -1;
153 }
154
155 val = (val <= 0 ? 60 : val);
156
157 sh_portchk_interval = (time_t) val;
158 SL_RETURN(0, _("sh_portchk_set_interval"));
159}
160
161
162static int sh_portchk_set_active (const char * str)
163{
164 return sh_util_flagval(str, &sh_portchk_active);
165}
166
167static int sh_portchk_set_udp (const char * str)
168{
169 return sh_util_flagval(str, &sh_portchk_check_udp);
170}
171
172static int sh_portchk_set_severity (const char * str)
173{
174 char tmp[32];
175 tmp[0] = '='; tmp[1] = '\0';
176 sl_strlcat (tmp, str, 32);
177 return sh_error_set_level (tmp, &sh_portchk_severity);
178}
179
180sh_rconf sh_portchk_table[] = {
181 {
182 N_("severityportcheck"),
183 sh_portchk_set_severity,
184 },
185 {
186 N_("portcheckrequired"),
187 sh_portchk_add_required,
188 },
189 {
190 N_("portcheckoptional"),
191 sh_portchk_add_optional,
192 },
193 {
[127]194 N_("portcheckignore"),
195 sh_portchk_add_ignore,
196 },
197 {
[67]198 N_("portcheckactive"),
199 sh_portchk_set_active,
200 },
201 {
202 N_("portcheckinterface"),
203 sh_portchk_add_interface,
204 },
205 {
206 N_("portcheckinterval"),
207 sh_portchk_set_interval,
208 },
209 {
210 N_("portcheckudp"),
211 sh_portchk_set_udp,
212 },
213 {
214 NULL,
215 NULL
216 }
217};
218
219#endif
220
221/* Interface to initialize port check
222 */
223int sh_portchk_init ();
224
225/* Interface to reset port check
226 */
227int sh_portchk_reset ();
228
229/* Interface to run port check
230 */
231int sh_portchk_check ();
232
233
234static char * check_services (int port, char * proto);
235
236#ifdef TEST_ONLY
237
238static int portchk_debug = 0;
239#define SH_ALLOC malloc
240#define SH_FREE free
241#define sh_util_strdup strdup
242#define sl_strlcpy strncpy
243#define _(a) a
244
245#else
246
247static int portchk_debug = 0;
248
249#endif
250
251static void sh_portchk_add_to_list (char * proto,
252 int port, struct in_addr haddr, char * service,
253 int flag, int status)
254{
255 struct sh_portentry * new = SH_ALLOC (sizeof(struct sh_portentry));
256
257 if (portchk_debug)
258 fprintf(stderr, _("add to list: port %d/%s %d %d (%s)\n"),
259 port, proto, flag, status, service ? service : _("undef"));
260
261 new->port = port;
262 sl_strlcpy (new->interface, inet_ntoa(haddr), SH_INTERFACE_SIZE);
263 new->status = status;
264 new->flag = flag;
265
266 new->error = NULL;
267
268 if (service)
269 new->service = sh_util_strdup (service);
270 else
271 new->service = NULL;
272 if (0 == strcmp(proto, "tcp"))
273 {
274 new->next = portlist_tcp;
275 portlist_tcp = new;
276 }
277 else
278 {
279 new->next = portlist_udp;
280 portlist_udp = new;
281 }
282 return;
283}
284
285/* Reset the list by setting all entries to UNKN.
286 * In the next cycle we will check, and set found ports to ISOK.
287 * Thereafter, we check for entries that are still UNKN.
288 */
289static void sh_portchk_reset_lists ()
290{
291 struct sh_portentry * portlist;
292
293 portlist = portlist_tcp;
294 while (portlist)
295 {
296 if (portlist->status != SH_PORT_MISS)
297 portlist->status = SH_PORT_UNKN;
298 portlist = portlist->next;
299 }
300 portlist = portlist_udp;
301 while (portlist)
302 {
303 if (portlist->status != SH_PORT_MISS)
304 portlist->status = SH_PORT_UNKN;
305 portlist = portlist->next;
306 }
307 return;
308}
309
310static struct sh_portentry * sh_portchk_kill_list (struct sh_portentry * head)
311{
312 if (head)
313 {
314 if (head->next)
315 sh_portchk_kill_list (head->next);
316
317 if (head->service)
318 SH_FREE(head->service);
319 SH_FREE(head);
320 }
321 return NULL;
322}
323
324/* check the list of open ports for any that are marked as UNKN
325 */
326static void sh_portchk_check_list (struct sh_portentry ** head, char * proto, int report)
327{
328 struct sh_portentry * ptr = *head;
329 struct sh_portentry * pre = *head;
330 char errbuf[256];
331
332 while (ptr)
333 {
334 if (portchk_debug && report)
335 fprintf(stderr, _("check list: port %d/%s %d %d\n"),
336 ptr->port, proto, ptr->flag, ptr->status);
337
338 if (ptr->status == SH_PORT_UNKN)
339 {
340 /* Don't report missing ports that are marked as optional
341 */
[127]342 if (ptr->flag != SH_PORT_OPT && ptr->flag != SH_PORT_IGN)
[67]343 {
344 snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceMissing] port %s:%d/%s (%s)"),
345 ptr->interface, ptr->port, proto,
346 ptr->service ? ptr->service : check_services(ptr->port, proto));
347#ifdef TEST_ONLY
348 if (report == SH_PORT_REPORT)
349 fprintf(stderr, _("%s\n"), errbuf);
350#else
351 if (report == SH_PORT_REPORT)
352 sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0,
353 MSG_PORT_REPORT, errbuf);
354#endif
355 }
356
357 ptr->status = SH_PORT_MISS;
358
[128]359 if ((ptr->flag != SH_PORT_REQ) && (ptr->flag != SH_PORT_OPT) && (ptr->flag != SH_PORT_IGN))
[67]360 {
361 if (portchk_debug && report)
362 fprintf(stderr, _("removing: port %d/%s %d %d\n"),
363 ptr->port, proto, ptr->flag, ptr->status);
364
365 if (ptr == *head)
366 {
367 *head = ptr->next;
368 if (ptr->service)
369 SH_FREE(ptr->service);
370 SH_FREE(ptr);
371 ptr = *head;
372 pre = *head;
373 continue;
374 }
375 else if (ptr->next == NULL)
376 {
377 pre->next = NULL;
378 if (ptr->service)
379 SH_FREE(ptr->service);
380 SH_FREE(ptr);
381 return;
382 }
383 else
384 {
385 pre->next = ptr->next;
386 if (ptr->service)
387 SH_FREE(ptr->service);
388 SH_FREE(ptr);
389 ptr = pre->next;
390 continue;
391 }
392 }
393 }
394 pre = ptr;
395 ptr = ptr->next;
396 }
397 return;
398}
399
400
401static struct sh_portentry * sh_portchk_get_from_list (char * proto, int port,
402 struct in_addr haddr, char * service)
403{
404 struct sh_portentry * portlist;
[75]405 char iface_all[8];
406
407 sl_strlcpy (iface_all, _("0.0.0.0"), sizeof(iface_all));
[67]408
409 if (0 == strcmp(proto, "tcp"))
410 portlist = portlist_tcp;
411 else
412 portlist = portlist_udp;
413
414 if (service)
415 {
416 while (portlist)
417 {
418 if (portlist->service &&
419 0 == strcmp(service, portlist->service) &&
420 (0 == strcmp(portlist->interface, inet_ntoa(haddr)) ||
421 0 == strcmp(portlist->interface, iface_all)))
422 return portlist;
423 portlist = portlist->next;
424 }
425 }
426 else
427 {
428 while (portlist)
429 {
430 if (port == portlist->port &&
431 (0 == strcmp(portlist->interface, inet_ntoa(haddr)) ||
432 0 == strcmp(portlist->interface, iface_all)))
433 return portlist;
434 portlist = portlist->next;
435 }
436 }
437 return NULL;
438}
439
440
441static void sh_portchk_cmp_to_list (char * proto, int port, struct in_addr haddr, char * service)
442{
443 struct sh_portentry * portent;
444 char errbuf[256];
445
446
447 portent = sh_portchk_get_from_list (proto, port, haddr, service);
448
449 if (service)
450 {
451 if (!portent)
452 {
453 snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceNew] port %s:%d/%s (%s)"),
454 inet_ntoa(haddr), port, proto, service);
455#ifdef TEST_ONLY
456 fprintf(stderr, _("open port: %s:%d/%s (%s)\n"),
457 inet_ntoa(haddr), port, proto, service);
458#else
459 sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0,
460 MSG_PORT_REPORT, errbuf);
461#endif
462 /*
463 * was not there, thus it is not in 'required' or 'optional' list
464 */
465 sh_portchk_add_to_list (proto, port, haddr, service, SH_PORT_NOT, SH_PORT_ISOK);
466 }
[127]467 else if (portent->status == SH_PORT_MISS && portent->flag != SH_PORT_IGN)
[67]468 {
469 snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceRestarted] port %s:%d/%s to %d/%s (%s)"),
470 inet_ntoa(haddr), portent->port, proto, port, proto, service);
471#ifdef TEST_ONLY
472 fprintf(stderr, _("service: %s\n"), errbuf);
473#else
474 sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0,
475 MSG_PORT_REPORT, errbuf);
476#endif
477
478 portent->status = SH_PORT_ISOK;
479 }
480 else if (port != portent->port && (-1) != portent->port)
481 {
482 snprintf (errbuf, sizeof(errbuf), _("POLICY [ServicePortSwitch] port %s:%d/%s to %d/%s (%s)"),
483 inet_ntoa(haddr), portent->port, proto, port, proto, service);
484#ifdef TEST_ONLY
485 fprintf(stderr, _("service: %s\n"), errbuf);
486#else
487 sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0,
488 MSG_PORT_REPORT, errbuf);
489#endif
[127]490 portent->port = port;
[67]491 portent->status = SH_PORT_ISOK;
492 }
493 else
494 {
495 portent->status = SH_PORT_ISOK;
496 }
497 }
498 else
499 {
500 if (!portent)
501 {
502 snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceNew] port %s:%d/%s (%s)"),
503 inet_ntoa(haddr), port, proto, check_services(port, proto));
504#ifdef TEST_ONLY
505 fprintf(stderr, _("open port: %s:%d/%s (%s)\n"),
506 inet_ntoa(haddr), port, proto, check_services(port, proto));
507#else
508 sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0,
509 MSG_PORT_REPORT, errbuf);
510#endif
511
512 /* was not there, thus it is not in 'required' or 'optional' list
513 */
514 sh_portchk_add_to_list (proto, port, haddr, service, SH_PORT_NOT, SH_PORT_ISOK);
515 }
[127]516 else if (portent->status == SH_PORT_MISS && portent->flag != SH_PORT_IGN)
[67]517 {
518 snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceRestarted] port %s:%d/%s (%s)"),
519 inet_ntoa(haddr), port, proto, check_services(port, proto));
520#ifdef TEST_ONLY
521 fprintf(stderr, _("port : %s\n"), errbuf);
522#else
523 sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0,
524 MSG_PORT_REPORT, errbuf);
525#endif
526
527 portent->status = SH_PORT_ISOK;
528 }
529 else
530 {
531 portent->status = SH_PORT_ISOK;
532 }
533 }
534
535 return;
536}
537
538
539/* Returns a static buffer containing the name of the service
540 * running on port <port> (from /etc/services)
541 * Returns NULL on failure
542 */
543static char * check_services (int port, char * proto)
544{
545 static char buf[256];
546 struct servent * service = getservbyport(htons(port), proto);
547
548 if (service && service->s_name && service->s_name[0] != '\0')
549 {
550 snprintf (buf, sizeof(buf), _("maybe_%s"), service->s_name);
551 }
552 else
553 {
554 snprintf (buf, sizeof(buf), _("unknown"));
555 }
556 return buf;
557}
558
559/* Returns a static buffer containing the name of the service
560 * running on port <port> at <address> (from portmap daemon)
561 * Returns NULL on failure
562 */
563static char * check_rpc_list (int port, struct sockaddr_in * address,
564 unsigned long prot)
565{
566 struct pmaplist * head;
567 struct rpcent *r;
568 static char buf[256];
569
570 head = pmap_getmaps(address);
571
572 if (head)
573 {
574 do /* while (head != NULL) */
575 {
576 if ((head->pml_map.pm_prot == prot) &&
577 (port == (int)head->pml_map.pm_port))
578 {
579 r = getrpcbynumber((int)head->pml_map.pm_prog);
580 if (r && r->r_name && r->r_name[0] != '\0')
581 {
582 snprintf (buf, sizeof(buf), "%s", r->r_name);
583 return buf;
584 }
585 else
586 {
587 snprintf (buf, sizeof(buf), "RPC_%lu",
588 (unsigned long)head->pml_map.pm_prog);
589 return buf;
590 }
591 }
592 head = head->pml_next;
593 }
594 while (head != NULL);
595 }
596
597 return NULL;
598}
599
600static int check_port_udp_internal (int fd, int port, struct in_addr haddr)
601{
602 struct sockaddr_in sinr;
603 /* struct in_addr haddr; */
604 int retval;
605 char * p;
606 char buf[8];
[78]607#ifndef TEST_ONLY
608 char errmsg[256];
609 int nerr;
610#endif
[132]611 char errbuf[SH_ERRBUF_SIZE];
[67]612
613 /* inet_aton(interface, &haddr); */
614
615 sinr.sin_family = AF_INET;
616 sinr.sin_port = htons (port);
617 sinr.sin_addr = haddr;
618
[78]619 do {
620 retval = connect(fd, (struct sockaddr *) &sinr, sizeof(sinr));
621 } while (retval < 0 && errno == EINTR);
622
[67]623 if (retval == -1)
624 {
625#ifdef TEST_ONLY
626 if (portchk_debug)
627 perror(_("connect"));
628#else
[78]629 nerr = errno;
630 sl_snprintf(errmsg, sizeof(errmsg), _("check port: %5d/udp on %15s: %s"),
[132]631 port, inet_ntoa(haddr), sh_error_message(errno, errbuf, sizeof(errbuf)));
[78]632 sh_error_handle((-1), FIL__, __LINE__, nerr, MSG_E_SUBGEN,
633 errmsg, _("connect"));
[67]634#endif
635 }
636 else
637 {
[78]638 do {
639 retval = send (fd, buf, 0, 0);
640 } while (retval < 0 && errno == EINTR);
[67]641
642 if (retval == -1 && errno == ECONNREFUSED)
643 {
644 if (portchk_debug)
645 fprintf(stderr, _("check port: %5d/udp on %15s established/time_wait\n"),
646 port, inet_ntoa(haddr));
647 }
648 else
649 {
[78]650 /* Only the second send() may catch the error
651 */
652 do {
653 retval = send (fd, buf, 0, 0);
654 } while (retval < 0 && errno == EINTR);
655
[67]656 if (retval == -1 && errno == ECONNREFUSED)
657 {
658 if (portchk_debug)
659 fprintf(stderr, _("check port: %5d/udp on %15s established/time_wait\n"),
660 port, inet_ntoa(haddr));
661 }
662 else if (retval != -1)
663 {
664 /* Try to get service name from portmap
665 */
666 p = check_rpc_list (port, &sinr, IPPROTO_UDP);
667
668 sh_portchk_cmp_to_list ("udp", port, haddr, p ? p : NULL);
669
670 /* If not an RPC service, try to get name from /etc/services
671 */
672 if (!p)
673 p = check_services(port, "udp");
674
675 if (portchk_debug)
676 fprintf(stderr, _("check port: %5d/udp on %15s open %s\n"),
677 port, inet_ntoa(haddr), p);
678
679 }
680 }
681 }
682 close (fd);
683 return 0;
684}
685
686static int check_port_tcp_internal (int fd, int port, struct in_addr haddr)
687{
688 struct sockaddr_in sinr;
689 /* struct in_addr haddr; */
690 int retval;
[109]691 int flags;
[67]692 char * p;
[78]693#ifndef TEST_ONLY
694 char errmsg[256];
695 int nerr;
696#endif
[132]697 char errbuf[SH_ERRBUF_SIZE];
[67]698
699 /* inet_aton(interface, &haddr); */
700
701 sinr.sin_family = AF_INET;
702 sinr.sin_port = htons (port);
703 sinr.sin_addr = haddr;
704
[78]705 do {
706 retval = connect(fd, (struct sockaddr *) &sinr, sizeof(sinr));
707 } while (retval < 0 && errno == EINTR);
708
[67]709 if (retval == -1 && errno == ECONNREFUSED)
710 {
711 if (portchk_debug)
712 fprintf(stderr, _("check port: %5d on %15s established/time_wait\n"),
713 port, inet_ntoa(haddr));
714 }
715 else if (retval == -1)
716 {
717#ifdef TEST_ONLY
718 if (portchk_debug)
719 perror(_("connect"));
720#else
[78]721 nerr = errno;
722 sl_snprintf(errmsg, sizeof(errmsg), _("check port: %5d/tcp on %15s: %s"),
[132]723 port, inet_ntoa(haddr), sh_error_message(errno, errbuf, sizeof(errbuf)));
[78]724 sh_error_handle((-1), FIL__, __LINE__, nerr, MSG_E_SUBGEN,
725 errmsg, _("connect"));
[67]726#endif
727 }
728 else
729 {
730 /* Try to get service name from portmap
731 */
732 p = check_rpc_list (port, &sinr, IPPROTO_TCP);
733
734 sh_portchk_cmp_to_list ("tcp", port, haddr, p ? p : NULL);
735
736 /* If not an RPC service, try to get name from /etc/services
737 */
738 if (!p)
739 p = check_services(port, "tcp");
740
741 if (portchk_debug)
742 fprintf(stderr, _("check port: %5d on %15s open %s\n"),
743 port, inet_ntoa(haddr), p);
[109]744
745#if !defined(O_NONBLOCK)
746#if defined(O_NDELAY)
747#define O_NONBLOCK O_NDELAY
748#else
749#define O_NONBLOCK 0
750#endif
751#endif
752
753 /* prepare to close connection gracefully
754 */
755 if (port == 22) /* ssh */
756 {
757 flags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0);
758 retry_fcntl(FIL__, __LINE__, fd, F_SETFL, flags | O_NONBLOCK);
759 write (fd, _("SSH-2.0-Foobar"), 14);
760 write (fd, "\r\n", 2);
761 }
762 else if (port == 25) /* smtp */
763 {
764 flags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0);
765 retry_fcntl(FIL__, __LINE__, fd, F_SETFL, flags | O_NONBLOCK);
766 write (fd, _("QUIT"), 4);
767 write (fd, "\r\n", 2);
768 }
769 else if (port == 79) /* finger */
770 {
771 flags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0);
772 retry_fcntl(FIL__, __LINE__, fd, F_SETFL, flags | O_NONBLOCK);
773 write (fd, "\r\n", 2);
774 }
775 else if (port == 110) /* pop3 */
776 {
777 flags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0);
778 retry_fcntl(FIL__, __LINE__, fd, F_SETFL, flags | O_NONBLOCK);
779 write (fd, _("QUIT"), 4);
780 write (fd, "\r\n", 2);
781 }
782 else if (port == 143) /* imap */
783 {
784 flags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0);
785 retry_fcntl(FIL__, __LINE__, fd, F_SETFL, flags | O_NONBLOCK);
786 write (fd, _("A01 LOGOUT"), 10);
787 write (fd, "\r\n", 2);
788 }
[67]789 }
790 close (fd);
791 return 0;
792}
793
794/* typedef uint32_t in_addr_t;
795 * struct in_addr
796 * {
797 * in_addr_t s_addr;
798 * };
799 */
800
801#define SH_IFACE_MAX 16
802
803struct portchk_interfaces {
804 struct in_addr iface[SH_IFACE_MAX];
805 int used;
806};
807
808static struct portchk_interfaces iface_list;
809static int iface_initialized = 0;
810
811#ifdef TEST_ONLY
812static char * portchk_hostname = NULL;
813#else
814static char * portchk_hostname = sh.host.name;
815#endif
816
[140]817int sh_portchk_init (struct mod_type * arg)
[67]818{
819 struct hostent * hent;
820 int i = 0;
821 char errbuf[256];
[140]822 (void) arg;
[67]823
824 if (portchk_debug)
825 fprintf(stderr, _("checking ports on: %s\n"), portchk_hostname ? portchk_hostname : _("NULL"));
[78]826
[67]827 if (!portchk_hostname)
828 return -1;
829
[78]830 if (sh_portchk_active == S_FALSE)
831 return -1;
832
[67]833 if (iface_initialized == 0)
834 {
835 iface_list.used = 0;
836 iface_initialized = 1;
837 }
838
[134]839 SH_MUTEX_LOCK(mutex_resolv);
[67]840 hent = gethostbyname(portchk_hostname);
841
842 while (hent && hent->h_addr_list[i] && (iface_list.used < SH_IFACE_MAX))
843 {
844 memcpy (&(iface_list.iface[iface_list.used].s_addr), hent->h_addr_list[i], sizeof(in_addr_t));
[134]845 ++iface_list.used;
846 ++i;
847 }
848 SH_MUTEX_UNLOCK(mutex_resolv);
849
850 for (i = 0; i < iface_list.used; ++i)
851 {
[67]852 sl_snprintf(errbuf, sizeof(errbuf), _("interface: %s"),
[134]853 inet_ntoa(iface_list.iface[i]));
[67]854 sh_error_handle(SH_ERR_INFO, FIL__, __LINE__, 0, MSG_E_SUBGEN,
855 errbuf, _("sh_portchk_init"));
856 }
857
858 return 0;
859}
860
861#if !defined(TEST_ONLY)
862int sh_portchk_reconf ()
863{
[78]864 iface_initialized = 0;
[67]865
[78]866 sh_portchk_active = 1;
[80]867 sh_portchk_check_udp = 1;
[78]868
[67]869 portlist_udp = sh_portchk_kill_list (portlist_udp);
870 portlist_tcp = sh_portchk_kill_list (portlist_tcp);
871 return 0;
872}
873
874int sh_portchk_cleanup ()
875{
876 return sh_portchk_reconf ();
877}
878
879int sh_portchk_timer (time_t tcurrent)
880{
881 static time_t lastcheck = 0;
882
883 SL_ENTER(_("sh_portchk_timer"));
884 if ((time_t) (tcurrent - lastcheck) >= sh_portchk_interval)
885 {
886 lastcheck = tcurrent;
887 SL_RETURN((-1), _("sh_portchk_timer"));
888 }
889 SL_RETURN(0, _("sh_portchk_timer"));
890}
891#endif
892
893static int check_port_generic (int port, int type, int protocol)
894{
895 int i = 0;
896 int sock = -1;
897 int flag = 1; /* non-zero to enable an option */
898 struct in_addr haddr;
[132]899 char errbuf[SH_ERRBUF_SIZE];
900
[67]901 /* Check all interfaces for this host
902 */
903 while (i < iface_list.used)
904 {
905 if ((sock = socket(AF_INET, type, protocol)) < 0 )
906 {
907#ifdef TEST_ONLY
908 if (portchk_debug)
909 perror(_("socket"));
910#else
911 sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
[132]912 sh_error_message(errno, errbuf, sizeof(errbuf)), _("socket"));
[67]913#endif
914 }
915 if ( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
916 (void *) &flag, sizeof(flag)) < 0 )
917 {
918#ifdef TEST_ONLY
919 if (portchk_debug)
920 perror(_("setsockopt"));
921#else
922 sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
[132]923 sh_error_message(errno, errbuf, sizeof(errbuf)),_("setsockopt"));
[67]924#endif
925 }
926
927 memcpy (&(haddr.s_addr), &(iface_list.iface[i].s_addr), sizeof(in_addr_t));
928
929 if (protocol == IPPROTO_TCP)
930 check_port_tcp_internal(sock, port, haddr);
931 else
932 check_port_udp_internal(sock, port, haddr);
933
934 ++i;
935 }
936
937 return 0;
938}
939
940
941
942static int check_port_udp (int port)
943{
944 return check_port_generic(port, SOCK_DGRAM, IPPROTO_UDP);
945}
946
947static int check_port_tcp (int port)
948{
949 return check_port_generic(port, SOCK_STREAM, IPPROTO_TCP);
950}
951
952
953
954static int sh_portchk_scan_ports_generic (int min_port, int max_port, int type, int protocol)
955{
956 /*
957 int min_port = 1024;
958 int max_port = 65535;
959 */
960
961 int port;
962 int retval;
963 int sock = -1;
964 int flag = 1; /* non-zero to enable an option */
965
966 struct sockaddr_in addr;
967 int addrlen = sizeof(addr);
[132]968 char errbuf[SH_ERRBUF_SIZE];
[67]969
970 if (min_port == -1)
971 min_port = 0;
972 if (max_port == -1)
973 max_port = 65535;
974
975 for (port = min_port; port <= max_port; ++port)
976 {
977
978 if ((sock = socket(AF_INET, type, protocol)) < 0 )
979 {
980#ifdef TEST_ONLY
981 if (portchk_debug)
982 perror(_("socket"));
983#else
984 sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
[132]985 sh_error_message(errno, errbuf, sizeof(errbuf)), _("socket"));
[67]986#endif
987 }
988 if ( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
989 (void *) &flag, sizeof(flag)) < 0 )
990 {
991#ifdef TEST_ONLY
992 if (portchk_debug)
993 perror(_("setsockopt"));
994#else
995 sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
[132]996 sh_error_message(errno, errbuf, sizeof(errbuf)),_("setsockopt"));
[67]997#endif
998 }
999
1000 addr.sin_family = AF_INET;
1001 addr.sin_port = htons(port);
1002 addr.sin_addr.s_addr = INADDR_ANY;
1003
1004 retval = bind (sock, (struct sockaddr *) &addr, addrlen);
1005
1006 if (retval == 0)
1007 {
1008 /* we can bind the port, thus it is unused
1009 */
1010 close (sock);
1011 }
1012 else
1013 {
1014 if (errno == EINVAL || errno == EADDRINUSE)
1015 {
1016 /* try to connect to the port
1017 */
1018 if (protocol == IPPROTO_TCP)
1019 check_port_tcp(port);
1020 else
1021 check_port_udp(port);
1022 }
1023 else
1024 {
1025#ifdef TEST_ONLY
1026 if (portchk_debug)
1027 perror(_("bind"));
1028#else
1029 sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
[132]1030 sh_error_message(errno, errbuf, sizeof(errbuf)), _("bind"));
[67]1031#endif
1032 }
1033 close (sock);
1034 }
1035 }
1036 return 0;
1037}
1038
1039static int sh_portchk_scan_ports_tcp (int min_port, int max_port)
1040{
1041 return sh_portchk_scan_ports_generic (min_port, max_port, SOCK_STREAM, IPPROTO_TCP);
1042}
1043
1044static int sh_portchk_scan_ports_udp (int min_port, int max_port)
1045{
1046 return sh_portchk_scan_ports_generic (min_port, max_port, SOCK_DGRAM, IPPROTO_UDP);
1047}
1048
1049/* Subroutine to add an interface
1050 */
1051static int sh_portchk_add_interface (const char * str)
1052{
1053 struct in_addr haddr;
1054 char errbuf[256];
1055
1056 if (iface_initialized == 0)
1057 {
1058 iface_list.used = 0;
1059 iface_initialized = 1;
1060 }
1061
1062 if (0 == inet_aton(str, &haddr))
1063 return -1;
1064
1065 if (iface_list.used == SH_IFACE_MAX)
1066 return -1;
1067
1068 sl_snprintf(errbuf, sizeof(errbuf), _("interface: %s"), inet_ntoa(haddr));
1069 sh_error_handle(SH_ERR_INFO, FIL__, __LINE__, 0, MSG_E_SUBGEN,
1070 errbuf, _("sh_portchk_add_interface"));
1071
1072 memcpy (&(iface_list.iface[iface_list.used].s_addr), &(haddr.s_addr), sizeof(in_addr_t));
1073 ++iface_list.used;
1074
1075 return 0;
1076}
1077
1078
1079/* Subroutine to add a required or optional port/service
1080 */
1081static int sh_portchk_add_required_port_generic (char * service, char * interface, int type)
1082{
1083 char buf[256];
1084 char proto[4];
1085 char * p;
1086 char * endptr;
1087 unsigned long int port;
1088 struct in_addr haddr;
1089 struct sh_portentry * portent;
1090
1091 if (0 == inet_aton(interface, &haddr))
1092 return -1;
1093
1094 sl_strlcpy (buf, service, sizeof(buf));
1095
1096 p = strchr(buf, '/');
1097 if (!p)
1098 return -1;
1099 if (0 == strcmp(p, _("/tcp")))
1100 sl_strlcpy(proto, _("tcp"), sizeof(proto));
1101 else if (0 == strcmp(p, _("/udp")))
1102 sl_strlcpy(proto, _("udp"), sizeof(proto));
1103 else
1104 return -1;
1105
1106 *p = '\0';
1107 port = strtoul(buf, &endptr, 0);
1108
1109 if (*endptr != '\0')
1110 {
1111 portent = sh_portchk_get_from_list (proto, -1, haddr, buf);
1112 if (!portent)
1113 sh_portchk_add_to_list (proto, -1, haddr, buf, type, SH_PORT_UNKN);
1114 else
1115 {
1116#ifdef TEST_ONLY
1117 fprintf(stderr, "** WARNING: duplicate port definition %s/%s\n", buf, proto);
1118#else
1119 sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
1120 _("duplicate port definition"), _("sh_portchk_add_required_port_generic"));
1121#endif
1122 return -1;
1123 }
1124 }
1125 else if (port <= 65535)
1126 {
1127 portent = sh_portchk_get_from_list (proto, port, haddr, NULL);
1128 if (!portent)
1129 sh_portchk_add_to_list (proto, port, haddr, NULL, type, SH_PORT_UNKN);
1130 else
1131 {
1132#ifdef TEST_ONLY
1133 fprintf(stderr, "** WARNING: duplicate port definition %lu/%s\n", port, proto);
1134#else
1135 sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
1136 _("duplicate port definition"), _("sh_portchk_add_required_port_generic"));
1137#endif
1138 return -1;
1139 }
1140 }
1141 else
1142 return -1;
1143
1144 return 0;
1145}
1146
1147/* Internal interface to add required or optional ports as 'iface:portlist'
1148 */
1149static int sh_portchk_add_required_generic (const char * str, int type)
1150{
1151 size_t len;
1152 size_t ll = 0;
1153
1154 char * interface = NULL;
1155 char * list;
1156 char * p;
[131]1157#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_STRTOK_R)
1158 char * saveptr;
1159#endif
[67]1160
1161 if (!str)
1162 return -1;
1163
1164 if (strchr(str, ':'))
1165 {
1166 len = strlen(str);
1167 for (ll = 0; ll < len; ++ll)
1168 {
1169 if (str[ll] == ':' || str[ll] == ' ' || str[ll] == '\t')
1170 {
1171 interface = SH_ALLOC(ll+1);
1172 sl_strlcpy(interface, str, ll+1);
1173 interface[ll] = '\0';
1174 while (str[ll] == ':' || str[ll] == ' ' || str[ll] == '\t')
1175 ++ll;
1176 break;
1177 }
1178 }
1179 }
1180 else
1181 {
1182 interface = SH_ALLOC(8);
1183 sl_strlcpy(interface, _("0.0.0.0"), 8);
1184 interface[7] = '\0';
1185 while (str[ll] == ' ' || str[ll] == '\t')
1186 ++ll;
1187 }
1188
1189 if (!interface)
1190 return -1;
1191
1192 if (str[ll] == '\0')
1193 {
1194 SH_FREE(interface);
1195 return -1;
1196 }
1197
1198 if (portchk_debug)
1199 fprintf(stderr, "add ports for interface: %s\n", interface);
1200
1201 list = sh_util_strdup(&str[ll]);
[131]1202#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_STRTOK_R)
1203 p = strtok_r (list, " ,\t", &saveptr);
1204#else
[67]1205 p = strtok (list, " ,\t");
[131]1206#endif
[67]1207 if (!p)
1208 {
1209 SH_FREE(interface);
1210 SH_FREE(list);
1211 return -1;
1212 }
1213 while (p)
1214 {
1215 if (-1 == sh_portchk_add_required_port_generic (p, interface, type))
1216 {
1217 SH_FREE(interface);
1218 SH_FREE(list);
1219 return -1;
1220 }
[131]1221#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_STRTOK_R)
1222 p = strtok_r (NULL, " ,\t", &saveptr);
1223#else
[67]1224 p = strtok (NULL, " ,\t");
[131]1225#endif
[67]1226 }
1227 SH_FREE(interface);
1228 SH_FREE(list);
1229 return 0;
1230}
1231
1232/* User interface to add required ports as 'iface:portlist'
1233 */
1234static int sh_portchk_add_required (const char * str)
1235{
1236 return sh_portchk_add_required_generic (str, SH_PORT_REQ);
1237}
1238
1239/* User interface to add optional ports as 'iface:portlist'
1240 */
1241static int sh_portchk_add_optional (const char * str)
1242{
1243 return sh_portchk_add_required_generic (str, SH_PORT_OPT);
1244}
1245
[127]1246/* User interface to add ignoreable ports as 'iface:portlist'
1247 */
1248static int sh_portchk_add_ignore (const char * str)
1249{
1250 return sh_portchk_add_required_generic (str, SH_PORT_IGN);
1251}
1252
[67]1253/* Interface to run port check
1254 */
1255int sh_portchk_check ()
1256{
1257 int min_port = 0;
1258
[78]1259 if (sh_portchk_active != S_FALSE)
[67]1260 {
[78]1261 sh_portchk_reset_lists();
1262 if (0 != geteuid())
1263 {
1264 min_port = 1024;
[67]1265#ifdef TEST_ONLY
[78]1266 fprintf(stderr, "** WARNING not scanning ports < 1024\n");
[67]1267#else
[78]1268 sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
1269 _("not scanning ports below 1024"), _("sh_portchk_check"));
[67]1270#endif
[78]1271 }
1272 if (sh_portchk_check_udp == 1)
1273 sh_portchk_scan_ports_udp(min_port, -1);
1274 sh_portchk_scan_ports_tcp(min_port, -1);
1275 sh_portchk_check_list (&portlist_tcp, "tcp", SH_PORT_REPORT);
1276 if (sh_portchk_check_udp == 1)
1277 sh_portchk_check_list (&portlist_udp, "udp", SH_PORT_REPORT);
[67]1278 }
1279 return 0;
1280}
1281#endif
1282
1283#ifdef SH_CUTEST
1284#include "CuTest.h"
1285
1286void Test_portcheck_lists (CuTest *tc)
1287{
[73]1288#if defined(SH_USE_PORTCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
[67]1289 struct in_addr haddr_local;
1290 struct sh_portentry * portent;
1291
1292 CuAssertTrue(tc, 0 != inet_aton("127.0.0.1", &haddr_local));
1293
1294 sh_portchk_add_to_list ("tcp", 8000, haddr_local, NULL, SH_PORT_NOT, SH_PORT_UNKN);
1295
1296 portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, NULL);
1297 CuAssertPtrNotNull(tc, portent);
1298
1299 CuAssertTrue(tc, portent->port == 8000);
1300 CuAssertTrue(tc, 0 == strcmp("127.0.0.1", portent->interface));
1301 CuAssertTrue(tc, portent->status == SH_PORT_UNKN);
1302 CuAssertTrue(tc, portent->flag == SH_PORT_NOT);
1303
1304 sh_portchk_check_list (&portlist_tcp, "tcp", SH_PORT_NOREPT);
1305
1306 CuAssertTrue(tc, NULL == portlist_tcp);
1307
1308 sh_portchk_add_to_list ("tcp", 8000, haddr_local, NULL, SH_PORT_REQ, SH_PORT_UNKN);
1309 sh_portchk_add_to_list ("tcp", 8001, haddr_local, NULL, SH_PORT_NOT, SH_PORT_UNKN);
1310 sh_portchk_add_to_list ("tcp", 8002, haddr_local, NULL, SH_PORT_REQ, SH_PORT_UNKN);
1311 sh_portchk_add_to_list ("tcp", 8003, haddr_local, NULL, SH_PORT_NOT, SH_PORT_UNKN);
[127]1312 sh_portchk_add_to_list ("tcp", 8004, haddr_local, NULL, SH_PORT_IGN, SH_PORT_UNKN);
[67]1313 sh_portchk_add_to_list ("tcp", -1, haddr_local, "foo1", SH_PORT_NOT, SH_PORT_UNKN);
1314 sh_portchk_add_to_list ("tcp", -1, haddr_local, "foo2", SH_PORT_REQ, SH_PORT_UNKN);
1315 sh_portchk_add_to_list ("tcp", -1, haddr_local, "foo3", SH_PORT_NOT, SH_PORT_UNKN);
1316 sh_portchk_add_to_list ("tcp", -1, haddr_local, "foo4", SH_PORT_REQ, SH_PORT_UNKN);
[127]1317 sh_portchk_add_to_list ("tcp", -1, haddr_local, "foo5", SH_PORT_IGN, SH_PORT_UNKN);
[67]1318
1319 sh_portchk_check_list (&portlist_tcp, "tcp", SH_PORT_NOREPT);
1320
1321 CuAssertPtrNotNull(tc, portlist_tcp);
1322
1323 portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, NULL);
1324 CuAssertPtrNotNull(tc, portent);
1325
1326 portent = sh_portchk_get_from_list("tcp", 8001, haddr_local, NULL);
1327 CuAssertTrue(tc, NULL == portent);
1328
1329 portent = sh_portchk_get_from_list("tcp", 8002, haddr_local, NULL);
1330 CuAssertPtrNotNull(tc, portent);
1331
1332 portent = sh_portchk_get_from_list("tcp", 8003, haddr_local, NULL);
1333 CuAssertTrue(tc, NULL == portent);
1334
[127]1335 portent = sh_portchk_get_from_list("tcp", 8004, haddr_local, NULL);
1336 CuAssertPtrNotNull(tc, portent);
1337
[67]1338 portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, "foo1");
1339 CuAssertTrue(tc, NULL == portent);
1340
1341 portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, "foo2");
1342 CuAssertPtrNotNull(tc, portent);
1343 CuAssertTrue(tc, 0 == strcmp(portent->service, "foo2"));
1344
1345 portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, "foo3");
1346 CuAssertTrue(tc, NULL == portent);
1347
1348 portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, "foo4");
1349 CuAssertPtrNotNull(tc, portent);
1350 CuAssertTrue(tc, 0 == strcmp(portent->service, "foo4"));
[127]1351
1352 portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, "foo5");
1353 CuAssertPtrNotNull(tc, portent);
1354 CuAssertTrue(tc, 0 == strcmp(portent->service, "foo5"));
[67]1355#else
1356 (void) tc; /* fix compiler warning */
1357#endif
1358 return;
1359}
1360#endif
1361
1362#ifdef TEST_ONLY
1363
1364void usage (char * pname)
1365{
1366 printf ("%s [-r|--required interface:portlist][-o|--optional interface:portlist][--no-udp][-d|--debug] hostname\n\n", pname);
1367 printf (" Check local host for open ports; Version %s\n\n", PORTCHK_VERSION);
1368 printf (" Interface: Numeric address for an interface, e.g. 127.0.0.1\n");
1369 printf (" Portlist: List of ports or services, e.g. 22/tcp,nfs/udp,nlockmgr/udp\n");
1370 printf (" required -> must be open\n");
1371 printf (" optional -> may be open or closed\n");
1372 printf (" RPC services must be specified with service **name**, others with **port number**\n\n");
1373 printf (" Example:\n");
1374 printf (" %s --required 192.168.1.2:22/tcp,nfs/udp,nlockmgr/udp\n\n", pname);
1375 return;
1376}
1377
1378int main(int argc, char *argv[])
1379{
1380 char * pname = argv[0];
1381
1382
1383 /*
1384 test_lists();
1385
1386 portlist_tcp = sh_portchk_kill_list (portlist_tcp);
1387 portlist_udp = sh_portchk_kill_list (portlist_udp);
1388 */
1389
1390 // sh_portchk_add_required ("127.0.0.1 : nlockmgr/tcp, 5308/tcp, nfs/tcp");
1391
1392 while (argc > 1 && argv[1][0] == '-')
1393 {
1394 if (0 == strcmp(argv[1], "--help") || 0 == strcmp(argv[1], "-h"))
1395 {
1396 usage(pname);
1397 exit (0);
1398 }
1399 else if (0 == strcmp(argv[1], "--required") || 0 == strcmp(argv[1], "-r"))
1400 {
1401 if (argc < 3)
1402 {
1403 usage(pname);
1404 exit (1);
1405 }
1406 sh_portchk_add_required (argv[2]);
1407 --argc; ++argv;
1408 }
1409 else if (0 == strcmp(argv[1], "--optional") || 0 == strcmp(argv[1], "-o"))
1410 {
1411 if (argc < 3)
1412 {
1413 usage(pname);
1414 exit (1);
1415 }
1416 sh_portchk_add_optional (argv[2]);
1417 --argc; ++argv;
1418 }
1419 else if (0 == strcmp(argv[1], "--no-udp"))
1420 {
1421 sh_portchk_check_udp = 0;
1422 }
1423 else if (0 == strcmp(argv[1], "--debug") || 0 == strcmp(argv[1], "-d"))
1424 {
1425 portchk_debug = 1;
1426 }
1427 else
1428 {
1429 usage(pname);
1430 exit (1);
1431 }
1432 --argc; ++argv;
1433 }
1434
1435 if (argc < 2)
1436 {
1437 usage(pname);
1438 exit (1);
1439 }
1440
1441 portchk_hostname = argv[1];
1442
1443 if (0 != sh_portchk_init ())
1444 {
1445 usage(pname);
1446 exit (1);
1447 }
1448
1449 sh_portchk_check();
1450
1451 return 0;
1452}
1453#endif
Note: See TracBrowser for help on using the repository browser.