1 /*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 1995
5 * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the FreeBSD project
18 * 4. Neither the name of the author nor the names of any co-contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 */
35
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD: stable/12/usr.sbin/rpc.statd/procs.c 325966 2017-11-18 14:26:50Z pfg $");
38
39 #include <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <rpc/rpc.h>
45 #include <syslog.h>
46 #include <vis.h>
47 #include <netdb.h> /* for getaddrinfo() */
48 #include <sys/types.h>
49 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <arpa/inet.h>
52
53 #include "statd.h"
54
55 static const char *
from_addr(saddr)56 from_addr(saddr)
57 struct sockaddr *saddr;
58 {
59 static char inet_buf[INET6_ADDRSTRLEN];
60
61 if (getnameinfo(saddr, saddr->sa_len, inet_buf, sizeof(inet_buf),
62 NULL, 0, NI_NUMERICHOST) == 0)
63 return inet_buf;
64 return "???";
65 }
66
67 /* sm_check_hostname -------------------------------------------------------- */
68 /*
69 * Purpose: Check `mon_name' member of sm_name struct to ensure that the array
70 * consists only of printable characters.
71 *
72 * Returns: TRUE if hostname is good. FALSE if hostname contains binary or
73 * otherwise non-printable characters.
74 *
75 * Notes: Will syslog(3) to warn of corrupt hostname.
76 */
77
sm_check_hostname(struct svc_req * req,char * arg)78 int sm_check_hostname(struct svc_req *req, char *arg)
79 {
80 int len, dstlen, ret;
81 struct sockaddr *claddr;
82 char *dst;
83
84 len = strlen(arg);
85 dstlen = (4 * len) + 1;
86 dst = malloc(dstlen);
87 claddr = (struct sockaddr *) (svc_getrpccaller(req->rq_xprt)->buf) ;
88 ret = 1;
89
90 if (claddr == NULL || dst == NULL)
91 {
92 ret = 0;
93 }
94 else if (strvis(dst, arg, VIS_WHITE) != len)
95 {
96 syslog(LOG_ERR,
97 "sm_stat: client %s hostname %s contained invalid characters.",
98 from_addr(claddr),
99 dst);
100 ret = 0;
101 }
102 free(dst);
103 return (ret);
104 }
105
106 /* sm_stat_1 --------------------------------------------------------------- */
107 /*
108 Purpose: RPC call to enquire if a host can be monitored
109 Returns: TRUE for any hostname that can be looked up to give
110 an address.
111 */
112
sm_stat_1_svc(sm_name * arg,struct svc_req * req)113 struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req)
114 {
115 static sm_stat_res res;
116 struct addrinfo *ai;
117 struct sockaddr *claddr;
118 static int err;
119
120 err = 1;
121 if ((err = sm_check_hostname(req, arg->mon_name)) == 0)
122 {
123 res.res_stat = stat_fail;
124 }
125 if (err != 0)
126 {
127 if (debug)
128 syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
129 if (getaddrinfo(arg->mon_name, NULL, NULL, &ai) == 0) {
130 res.res_stat = stat_succ;
131 freeaddrinfo(ai);
132 }
133 else
134 {
135 claddr = (struct sockaddr *) (svc_getrpccaller(req->rq_xprt)->buf) ;
136 syslog(LOG_ERR, "invalid hostname to sm_stat from %s: %s",
137 from_addr(claddr), arg->mon_name);
138 res.res_stat = stat_fail;
139 }
140 }
141 res.state = status_info->ourState;
142 return (&res);
143 }
144
145 /* sm_mon_1 ---------------------------------------------------------------- */
146 /*
147 Purpose: RPC procedure to establish a monitor request
148 Returns: Success, unless lack of resources prevents
149 the necessary structures from being set up
150 to record the request, or if the hostname is not
151 valid (as judged by getaddrinfo())
152 */
153
sm_mon_1_svc(mon * arg,struct svc_req * req)154 struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
155 {
156 static sm_stat_res res;
157 HostInfo *hp;
158 static int err;
159 MonList *lp;
160 struct addrinfo *ai;
161
162 if ((err = sm_check_hostname(req, arg->mon_id.mon_name)) == 0)
163 {
164 res.res_stat = stat_fail;
165 }
166
167 if (err != 0)
168 {
169 if (debug)
170 {
171 syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
172 syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
173 arg->mon_id.my_id.my_name,
174 arg->mon_id.my_id.my_prog,
175 arg->mon_id.my_id.my_vers,
176 arg->mon_id.my_id.my_proc);
177 }
178 res.res_stat = stat_fail; /* Assume fail until set otherwise */
179 res.state = status_info->ourState;
180
181 /* Find existing host entry, or create one if not found */
182 /* If find_host() fails, it will have logged the error already. */
183 if (getaddrinfo(arg->mon_id.mon_name, NULL, NULL, &ai) != 0)
184 {
185 syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
186 return (&res);
187 }
188 freeaddrinfo(ai);
189 if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
190 {
191 lp = (MonList *)malloc(sizeof(MonList));
192 if (!lp)
193 {
194 syslog(LOG_ERR, "Out of memory");
195 }
196 else
197 {
198 strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
199 lp->notifyProg = arg->mon_id.my_id.my_prog;
200 lp->notifyVers = arg->mon_id.my_id.my_vers;
201 lp->notifyProc = arg->mon_id.my_id.my_proc;
202 memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
203
204 lp->next = hp->monList;
205 hp->monList = lp;
206 sync_file();
207
208 res.res_stat = stat_succ; /* Report success */
209 }
210 }
211 }
212 return (&res);
213 }
214
215 /* do_unmon ---------------------------------------------------------------- */
216 /*
217 Purpose: Remove a monitor request from a host
218 Returns: TRUE if found, FALSE if not found.
219 Notes: Common code from sm_unmon_1_svc and sm_unmon_all_1_svc
220 In the unlikely event of more than one identical monitor
221 request, all are removed.
222 */
223
do_unmon(HostInfo * hp,my_id * idp)224 static int do_unmon(HostInfo *hp, my_id *idp)
225 {
226 MonList *lp, *next;
227 MonList *last = NULL;
228 int result = FALSE;
229
230 lp = hp->monList;
231 while (lp)
232 {
233 if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
234 && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
235 && (idp->my_vers == lp->notifyVers))
236 {
237 /* found one. Unhook from chain and free. */
238 next = lp->next;
239 if (last) last->next = next;
240 else hp->monList = next;
241 free(lp);
242 lp = next;
243 result = TRUE;
244 }
245 else
246 {
247 last = lp;
248 lp = lp->next;
249 }
250 }
251 return (result);
252 }
253
254 /* sm_unmon_1 -------------------------------------------------------------- */
255 /*
256 Purpose: RPC procedure to release a monitor request.
257 Returns: Local machine's status number
258 Notes: The supplied mon_id should match the value passed in an
259 earlier call to sm_mon_1
260 */
261
sm_unmon_1_svc(mon_id * arg,struct svc_req * req __unused)262 struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req __unused)
263 {
264 static sm_stat res;
265 HostInfo *hp;
266
267 if (debug)
268 {
269 syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
270 syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
271 arg->mon_name,
272 arg->my_id.my_prog, arg->my_id.my_vers, arg->my_id.my_proc);
273 }
274
275 if ((hp = find_host(arg->mon_name, FALSE)))
276 {
277 if (do_unmon(hp, &arg->my_id)) sync_file();
278 else
279 {
280 syslog(LOG_ERR, "unmon request from %s, no matching monitor",
281 arg->my_id.my_name);
282 }
283 }
284 else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
285 arg->my_id.my_name, arg->mon_name);
286
287 res.state = status_info->ourState;
288
289 return (&res);
290 }
291
292 /* sm_unmon_all_1 ---------------------------------------------------------- */
293 /*
294 Purpose: RPC procedure to release monitor requests.
295 Returns: Local machine's status number
296 Notes: Releases all monitor requests (if any) from the specified
297 host and program number.
298 */
299
sm_unmon_all_1_svc(my_id * arg,struct svc_req * req __unused)300 struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req __unused)
301 {
302 static sm_stat res;
303 HostInfo *hp;
304 int i;
305
306 if (debug)
307 {
308 syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
309 arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
310 }
311
312 for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
313 {
314 do_unmon(hp, arg);
315 }
316 sync_file();
317
318 res.state = status_info->ourState;
319
320 return (&res);
321 }
322
323 /* sm_simu_crash_1 --------------------------------------------------------- */
324 /*
325 Purpose: RPC procedure to simulate a crash
326 Returns: Nothing
327 Notes: Standardised mechanism for debug purposes
328 The specification says that we should drop all of our
329 status information (apart from the list of monitored hosts
330 on disc). However, this would confuse the rpc.lockd
331 which would be unaware that all of its monitor requests
332 had been silently junked. Hence we in fact retain all
333 current requests and simply increment the status counter
334 and inform all hosts on the monitor list.
335 */
336
sm_simu_crash_1_svc(void * v __unused,struct svc_req * req __unused)337 void *sm_simu_crash_1_svc(void *v __unused, struct svc_req *req __unused)
338 {
339 static char dummy;
340 int work_to_do;
341 HostInfo *hp;
342 int i;
343
344 work_to_do = FALSE;
345 if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
346
347 /* Simulate crash by setting notify-required flag on all monitored */
348 /* hosts, and incrementing our status number. notify_hosts() is */
349 /* then called to fork a process to do the notifications. */
350
351 for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
352 {
353 if (hp->monList)
354 {
355 work_to_do = TRUE;
356 hp->notifyReqd = TRUE;
357 }
358 }
359 status_info->ourState += 2; /* always even numbers if not crashed */
360
361 if (work_to_do) notify_hosts();
362
363 return (&dummy);
364 }
365
366 /* sm_notify_1 ------------------------------------------------------------- */
367 /*
368 Purpose: RPC procedure notifying local statd of the crash of another
369 Returns: Nothing
370 Notes: There is danger of deadlock, since it is quite likely that
371 the client procedure that we call will in turn call us
372 to remove or adjust the monitor request.
373 We therefore fork() a process to do the notifications.
374 Note that the main HostInfo structure is in a mmap()
375 region and so will be shared with the child, but the
376 monList pointed to by the HostInfo is in normal memory.
377 Hence if we read the monList before forking, we are
378 protected from the parent servicing other requests
379 that modify the list.
380 */
381
sm_notify_1_svc(stat_chge * arg,struct svc_req * req __unused)382 void *sm_notify_1_svc(stat_chge *arg, struct svc_req *req __unused)
383 {
384 struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
385 CLIENT *cli;
386 static char dummy;
387 sm_status tx_arg; /* arg sent to callback procedure */
388 MonList *lp;
389 HostInfo *hp;
390 pid_t pid;
391
392 if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
393 arg->mon_name, arg->state);
394
395 hp = find_host(arg->mon_name, FALSE);
396 if (!hp)
397 {
398 /* Never heard of this host - why is it notifying us? */
399 syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
400 return (&dummy);
401 }
402 lp = hp->monList;
403 if (!lp) return (&dummy); /* We know this host, but have no */
404 /* outstanding requests. */
405 pid = fork();
406 if (pid == -1)
407 {
408 syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
409 return (NULL); /* no answer, the client will retry */
410 }
411 if (pid) return (&dummy); /* Parent returns */
412
413 while (lp)
414 {
415 tx_arg.mon_name = arg->mon_name;
416 tx_arg.state = arg->state;
417 memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
418 cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
419 if (!cli)
420 {
421 syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
422 clnt_spcreateerror(""));
423 }
424 else
425 {
426 if (clnt_call(cli, lp->notifyProc, (xdrproc_t)xdr_sm_status, &tx_arg,
427 (xdrproc_t)xdr_void, &dummy, timeout) != RPC_SUCCESS)
428 {
429 syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
430 lp->notifyHost);
431 }
432 clnt_destroy(cli);
433 }
434 lp = lp->next;
435 }
436
437 exit (0); /* Child quits */
438 }
439