1 /******************************************************************************
2 *
3 * Program: radius server check plugin for Nagios
4 * License: GPL
5 *
6 * License Information:
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * $Id$
23 *
24 *****************************************************************************/
26 const char *progname = "check_radius";
27 #define REVISION "$Revision$"
28 #define COPYRIGHT "1999-2001"
29 #define AUTHORS "Robert August Vincent II/Karl DeBisschop"
30 #define EMAIL "kdebisschop@users.sourceforge.net"
31 #define SUMMARY "Tests to see if a radius server is accepting connections.\n"
33 #define OPTIONS "\
34 -H host -F config_file -u username -p password\'\
35 [-P port] [-t timeout] [-r retries] [-e expect]"
37 #define LONGOPTIONS "\
38 -H, --hostname=HOST\n\
39 Host name argument for servers using host headers (use numeric\n\
40 address if possible to bypass DNS lookup).\n\
41 -P, --port=INTEGER\n\
42 Port number (default: %d)\n\
43 -u, --username=STRING\n\
44 The user to authenticate\n\
45 -p, --password=STRING\n\
46 Password for autentication (SECURITY RISK)\n\
47 -F, --filename=STRING\n\
48 Configuration file\n\
49 -e, --expect=STRING\n\
50 Response string to expect from the server\n\
51 -r, --retries=INTEGER\n\
52 Number of times to retry a failed connection\n\
53 -t, --timeout=INTEGER\n\
54 Seconds before connection times out (default: %d)\n\
55 -v, --verbose\n\
56 Show details for command-line debugging (do not use with nagios server)\n\
57 -h, --help\n\
58 Print detailed help screen\n\
59 -V, --version\n\
60 Print version information\n"
62 #define DESCRIPTION "\
63 This plugin tests a radius server to see if it is accepting connections.\n\
64 \n\
65 The server to test must be specified in the invocation, as well as a user\n\
66 name and password. A configuration file may also be present. The format of\n\
67 the configuration file is described in the radiusclient library sources.\n\
68 \n\
69 The password option presents a substantial security issue because the\n\
70 password can be determined by careful watching of the command line in\n\
71 a process listing. This risk is exacerbated because nagios will\n\
72 run the plugin at regular prdictable intervals. Please be sure that\n\
73 the password used does not allow access to sensitive system resources,\n\
74 otherwise compormise could occur.\n"
76 #include "config.h"
77 #include "common.h"
78 #include "utils.h"
79 #include <radiusclient.h>
81 int process_arguments (int, char **);
82 void print_usage (void);
83 void print_help (void);
85 char *server = NULL;
86 int port = PW_AUTH_UDP_PORT;
87 char *username = NULL;
88 char *password = NULL;
89 char *expect = NULL;
90 char *config_file = NULL;
91 int retries = 1;
92 int verbose = FALSE;
94 ENV *env = NULL;
96 /******************************************************************************
98 The (psuedo?)literate programming XML is contained within \@\@\- <XML> \-\@\@
99 tags in the comments. With in the tags, the XML is assembled sequentially.
100 You can define entities in tags. You also have all the #defines available as
101 entities.
103 Please note that all tags must be lowercase to use the DocBook XML DTD.
105 @@-<article>
107 <sect1>
108 <title>Quick Reference</title>
109 <!-- The refentry forms a manpage -->
110 <refentry>
111 <refmeta>
112 <manvolnum>5<manvolnum>
113 </refmeta>
114 <refnamdiv>
115 <refname>&progname;</refname>
116 <refpurpose>&SUMMARY;</refpurpose>
117 </refnamdiv>
118 </refentry>
119 </sect1>
121 <sect1>
122 <title>FAQ</title>
123 </sect1>
125 <sect1>
126 <title>Theory, Installation, and Operation</title>
128 <sect2>
129 <title>General Description</title>
130 <para>
131 &DESCRIPTION;
132 </para>
133 </sect2>
135 <sect2>
136 <title>Future Enhancements</title>
137 <para>Todo List</para>
138 <itemizedlist>
139 <listitem>Add option to get password from a secured file rather than the command line</listitem>
140 </itemizedlist>
141 </sect2>
144 <sect2>
145 <title>Functions</title>
146 -@@
147 ******************************************************************************/
149 int
150 main (int argc, char **argv)
151 {
152 UINT4 service;
153 char msg[BUFFER_LEN];
154 SEND_DATA data = { 0 };
155 int result;
156 UINT4 client_id;
158 if (process_arguments (argc, argv) == ERROR)
159 usage ("Could not parse arguments\n");
161 if ((config_file && rc_read_config (config_file)) ||
162 rc_read_dictionary (rc_conf_str ("dictionary")))
163 terminate (STATE_UNKNOWN, "Config file error");
165 service = PW_AUTHENTICATE_ONLY;
167 if (!(rc_avpair_add (&data.send_pairs, PW_SERVICE_TYPE, &service, 0) &&
168 rc_avpair_add (&data.send_pairs, PW_USER_NAME, username, 0) &&
169 rc_avpair_add (&data.send_pairs, PW_USER_PASSWORD, password, 0)))
170 terminate (STATE_UNKNOWN, "Out of Memory?");
172 /*
173 * Fill in NAS-IP-Address
174 */
176 if ((client_id = rc_own_ipaddress ()) == 0)
177 return (ERROR_RC);
179 if (rc_avpair_add (&(data.send_pairs), PW_NAS_IP_ADDRESS, &client_id, 0) ==
180 NULL) return (ERROR_RC);
182 rc_buildreq (&data, PW_ACCESS_REQUEST, server, port, timeout_interval,
183 retries);
185 result = rc_send_server (&data, msg);
186 rc_avpair_free (data.send_pairs);
187 if (data.receive_pairs)
188 rc_avpair_free (data.receive_pairs);
190 if (result == TIMEOUT_RC)
191 terminate (STATE_CRITICAL, "Timeout");
192 if (result == ERROR_RC)
193 terminate (STATE_CRITICAL, "Auth Error");
194 if (result == BADRESP_RC)
195 terminate (STATE_WARNING, "Auth Failed");
196 if (expect && !strstr (msg, expect))
197 terminate (STATE_WARNING, msg);
198 if (result == OK_RC)
199 terminate (STATE_OK, "Auth OK");
200 return (0);
201 }
205 /* process command-line arguments */
206 int
207 process_arguments (int argc, char **argv)
208 {
209 int c;
211 #ifdef HAVE_GETOPT_H
212 int option_index = 0;
213 static struct option long_options[] = {
214 {"hostname", required_argument, 0, 'H'},
215 {"port", required_argument, 0, 'P'},
216 {"username", required_argument, 0, 'u'},
217 {"password", required_argument, 0, 'p'},
218 {"filename", required_argument, 0, 'F'},
219 {"expect", required_argument, 0, 'e'},
220 {"retries", required_argument, 0, 'r'},
221 {"timeout", required_argument, 0, 't'},
222 {"verbose", no_argument, 0, 'v'},
223 {"version", no_argument, 0, 'V'},
224 {"help", no_argument, 0, 'h'},
225 {0, 0, 0, 0}
226 };
227 #endif
229 if (argc < 2)
230 return ERROR;
232 if (argc == 9) {
233 config_file = argv[1];
234 username = argv[2];
235 password = argv[3];
236 if (is_intpos (argv[4]))
237 timeout_interval = atoi (argv[4]);
238 else
239 usage ("Timeout interval must be a positive integer");
240 if (is_intpos (argv[5]))
241 retries = atoi (argv[5]);
242 else
243 usage ("Number of retries must be a positive integer");
244 server = argv[6];
245 if (is_intpos (argv[7]))
246 port = atoi (argv[7]);
247 else
248 usage ("Server port must be a positive integer");
249 expect = argv[8];
250 return OK;
251 }
253 while (1) {
254 #ifdef HAVE_GETOPT_H
255 c =
256 getopt_long (argc, argv, "+hVvH:P:F:u:p:t:r:e:", long_options,
257 &option_index);
258 #else
259 c = getopt (argc, argv, "+hVvH:P:F:u:p:t:r:e:");
260 #endif
262 if (c == -1 || c == EOF || c == 1)
263 break;
265 switch (c) {
266 case '?': /* print short usage statement if args not parsable */
267 printf ("%s: Unknown argument: %s\n\n", progname, optarg);
268 print_usage ();
269 exit (STATE_UNKNOWN);
270 case 'h': /* help */
271 print_help ();
272 exit (OK);
273 case 'V': /* version */
274 print_revision (progname, "$Revision$");
275 exit (OK);
276 case 'v': /* verbose mode */
277 verbose = TRUE;
278 break;
279 case 'H': /* hostname */
280 if (is_host (optarg) == FALSE) {
281 printf ("Invalid host name/address\n\n");
282 print_usage ();
283 exit (STATE_UNKNOWN);
284 }
285 server = optarg;
286 break;
287 case 'P': /* port */
288 if (is_intnonneg (optarg))
289 port = atoi (optarg);
290 else
291 usage ("Server port must be a positive integer");
292 break;
293 case 'u': /* username */
294 username = optarg;
295 break;
296 case 'p': /* password */
297 password = optarg;
298 break;
299 case 'F': /* configuration file */
300 config_file = optarg;
301 break;
302 case 'e': /* expect */
303 expect = optarg;
304 break;
305 case 'r': /* retries */
306 if (is_intpos (optarg))
307 retries = atoi (optarg);
308 else
309 usage ("Number of retries must be a positive integer");
310 break;
311 case 't': /* timeout */
312 if (is_intpos (optarg))
313 timeout_interval = atoi (optarg);
314 else
315 usage ("Timeout interval must be a positive integer");
316 break;
317 }
318 }
319 return OK;
320 }
321 \f
324 void
325 print_help (void)
326 {
327 print_revision (progname, REVISION);
328 printf
329 ("Copyright (c) %s %s <%s>\n\n%s\n",
330 COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
331 print_usage ();
332 printf
333 ("\nOptions:\n" LONGOPTIONS "\n" DESCRIPTION "\n",
334 port, timeout_interval);
335 support ();
336 }
339 void
340 print_usage (void)
341 {
342 printf ("Usage:\n" " %s %s\n"
343 #ifdef HAVE_GETOPT_H
344 " %s (-h | --help) for detailed help\n"
345 " %s (-V | --version) for version information\n",
346 #else
347 " %s -h for detailed help\n"
348 " %s -V for version information\n",
349 #endif
350 progname, OPTIONS, progname, progname);
351 }