kern.c revision 86187
1/*-
2 * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. Berkeley Software Design Inc's name may not be used to endorse or
13 *    promote products derived from this software without specific prior
14 *    written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 *      from BSDI kern.c,v 1.2 1998/11/25 22:38:27 don Exp
29 * $FreeBSD: head/usr.sbin/rpc.lockd/kern.c 86187 2001-11-08 10:34:21Z alfred $
30 */
31
32#include <sys/param.h>
33#include <sys/mount.h>
34#include <sys/queue.h>
35#include <sys/socket.h>
36#include <sys/stat.h>
37
38#include <err.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <syslog.h>
45#include <unistd.h>
46
47#include "nlm_prot.h"
48#include <nfs/rpcv2.h>
49#include <nfs/nfsproto.h>
50#include <nfsclient/nfs_lock.h>
51
52#include "lockd.h"
53#include "lockd_lock.h"
54#include <nfsclient/nfs.h>
55
56#define nfslockdans(_v, _ansp)	\
57	((_ansp)->la_vers = _v, \
58	nfsclnt(NFSCLNT_LOCKDANS, _ansp))
59
60/* Lock request owner. */
61typedef struct __owner {
62	pid_t	 pid;				/* Process ID. */
63	time_t	 tod;				/* Time-of-day. */
64} OWNER;
65static OWNER owner;
66
67static char hostname[MAXHOSTNAMELEN + 1];	/* Hostname. */
68
69int	lock_request(LOCKD_MSG *);
70int	test_request(LOCKD_MSG *);
71void	show(LOCKD_MSG *);
72int	unlock_request(LOCKD_MSG *);
73
74/*
75 * will break because fifo needs to be repopened when EOF'd
76 */
77#ifdef SETUID_DAEMON
78#define lockd_seteuid(uid)	seteuid(uid)
79#else
80#define lockd_seteuid(uid)	(1)
81#endif
82
83#define d_calls (debug_level > 1)
84#define d_args (debug_level > 2)
85
86#define from_addr(sockaddr) \
87	(inet_ntoa((sockaddr)->sin_addr))
88
89void
90client_cleanup(sig, code)
91	int sig;
92	int code;
93{
94	(void)lockd_seteuid(0);
95	(void)unlink(_PATH_LCKFIFO);
96	exit(-1);
97}
98
99/*
100 * client_request --
101 *	Loop around messages from the kernel, forwarding them off to
102 *	NLM servers.
103 */
104pid_t
105client_request(void)
106{
107	LOCKD_MSG msg;
108	fd_set rdset;
109	int fd, nr, ret;
110	pid_t child;
111
112	/* Recreate the NLM fifo. */
113	(void)unlink(_PATH_LCKFIFO);
114	(void)umask(S_IXGRP|S_IXOTH);
115	if (mkfifo(_PATH_LCKFIFO, S_IWUSR | S_IRUSR)) {
116		syslog(LOG_ERR, "mkfifo: %s: %m", _PATH_LCKFIFO);
117		exit (1);
118	}
119
120	/*
121	 * Create a separate process, the client code is really a separate
122	 * daemon that shares a lot of code.
123	 */
124	switch (child = fork()) {
125	case -1:
126		err(1, "fork");
127	case 0:
128		break;
129	default:
130		return (child);
131	}
132
133	signal(SIGHUP, client_cleanup);
134	signal(SIGTERM, client_cleanup);
135
136	/* Setup. */
137	(void)time(&owner.tod);
138	owner.pid = getpid();
139	(void)gethostname(hostname, sizeof(hostname) - 1);
140
141reopen:
142	/* Open the fifo for reading. */
143	if ((fd = open(_PATH_LCKFIFO, O_RDONLY /* | O_NONBLOCK */)) < 0)
144		syslog(LOG_ERR, "open: %s: %m", _PATH_LCKFIFO);
145
146	/* drop our root priviledges */
147	(void)lockd_seteuid(daemon_uid);
148
149	/* Set up the select. */
150	FD_ZERO(&rdset);
151
152	for (;;) {
153		/* Wait for contact... fifo's return EAGAIN when read with
154		 * no data
155		 */
156		FD_SET(fd, &rdset);
157		(void)select(fd + 1, &rdset, NULL, NULL, NULL);
158
159		/* Read the fixed length message. */
160		if ((nr = read(fd, &msg, sizeof(msg))) == sizeof(msg)) {
161			if (d_args)
162				show(&msg);
163
164			if (msg.lm_version != LOCKD_MSG_VERSION) {
165				syslog(LOG_ERR,
166				    "unknown msg type: %d", msg.lm_version);
167			}
168			/*
169			 * Send it to the NLM server and don't grant the lock
170			 * if we fail for any reason.
171			 */
172			switch (msg.lm_fl.l_type) {
173			case F_RDLCK:
174			case F_WRLCK:
175				if (msg.lm_getlk)
176					ret = test_request(&msg);
177				else
178					ret = lock_request(&msg);
179				break;
180			case F_UNLCK:
181				ret = unlock_request(&msg);
182				break;
183			default:
184				ret = 1;
185				syslog(LOG_ERR,
186				    "unknown lock type: %d", msg.lm_fl.l_type);
187				break;
188			}
189			if (ret) {
190				struct lockd_ans ans;
191
192				ans.la_msg_ident = msg.lm_msg_ident;
193				ans.la_errno = EHOSTUNREACH;
194
195				if (nfslockdans(LOCKD_ANS_VERSION, &ans)) {
196					syslog((errno == EPIPE ? LOG_INFO :
197						LOG_ERR), "process %lu: %m",
198						(u_long)msg.lm_msg_ident.pid);
199				}
200			}
201		} else if (nr == -1) {
202			if (errno != EAGAIN) {
203				syslog(LOG_ERR, "read: %s: %m", _PATH_LCKFIFO);
204				goto err;
205			}
206		} else if (nr != 0) {
207			syslog(LOG_ERR,
208			    "%s: discard %d bytes", _PATH_LCKFIFO, nr);
209		} if (nr == 0) {
210			close (fd);
211			goto reopen;
212		}
213	}
214
215	/* Reached only on error. */
216err:
217	(void)lockd_seteuid(0);
218	(void)unlink(_PATH_LCKFIFO);
219	_exit (1);
220}
221
222void
223set_auth(cl, ucred)
224	CLIENT *cl;
225	struct ucred *ucred;
226{
227        if (cl->cl_auth != NULL)
228                cl->cl_auth->ah_ops->ah_destroy(cl->cl_auth);
229        cl->cl_auth = authunix_create(hostname,
230                        ucred->cr_uid,
231                        ucred->cr_groups[0],
232                        ucred->cr_ngroups-1,
233                        &ucred->cr_groups[1]);
234}
235
236
237/*
238 * test_request --
239 *	Convert a lock LOCKD_MSG into an NLM request, and send it off.
240 */
241int
242test_request(LOCKD_MSG *msg)
243{
244	CLIENT *cli;
245	struct timeval timeout = {0, 0};	/* No timeout, no response. */
246	char dummy;
247
248	if (d_calls)
249		syslog(LOG_DEBUG, "test request: %s: %s to %s",
250		    msg->lm_nfsv3 ? "V4" : "V1/3",
251		    msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
252		    from_addr((struct sockaddr_in *)&msg->lm_addr));
253
254	if (msg->lm_nfsv3) {
255		struct nlm4_testargs arg4;
256
257		arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
258		arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
259		arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
260		arg4.alock.caller_name = hostname;
261		arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
262		arg4.alock.fh.n_len = msg->lm_fh_len;
263		arg4.alock.oh.n_bytes = (char *)&owner;
264		arg4.alock.oh.n_len = sizeof(owner);
265		arg4.alock.svid = msg->lm_msg_ident.pid;
266		arg4.alock.l_offset = msg->lm_fl.l_start;
267		arg4.alock.l_len = msg->lm_fl.l_len;
268
269		if ((cli = get_client(
270		    (struct sockaddr *)&msg->lm_addr,
271		    NLM_VERS4)) == NULL)
272			return (1);
273
274		set_auth(cli, &msg->lm_cred);
275		(void)clnt_call(cli, NLM_TEST_MSG,
276		    xdr_nlm4_testargs, &arg4, xdr_void, &dummy, timeout);
277	} else {
278		struct nlm_testargs arg;
279
280		arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
281		arg.cookie.n_len = sizeof(msg->lm_msg_ident);
282		arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
283		arg.alock.caller_name = hostname;
284		arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
285		arg.alock.fh.n_len = msg->lm_fh_len;
286		arg.alock.oh.n_bytes = (char *)&owner;
287		arg.alock.oh.n_len = sizeof(owner);
288		arg.alock.svid = msg->lm_msg_ident.pid;
289		arg.alock.l_offset = msg->lm_fl.l_start;
290		arg.alock.l_len = msg->lm_fl.l_len;
291
292		if ((cli = get_client(
293		    (struct sockaddr *)&msg->lm_addr,
294		    NLM_VERS)) == NULL)
295			return (1);
296
297		set_auth(cli, &msg->lm_cred);
298		(void)clnt_call(cli, NLM_TEST_MSG,
299		    xdr_nlm_testargs, &arg, xdr_void, &dummy, timeout);
300	}
301	return (0);
302}
303
304/*
305 * lock_request --
306 *	Convert a lock LOCKD_MSG into an NLM request, and send it off.
307 */
308int
309lock_request(LOCKD_MSG *msg)
310{
311	CLIENT *cli;
312	struct nlm4_lockargs arg4;
313	struct nlm_lockargs arg;
314	struct timeval timeout = {0, 0};	/* No timeout, no response. */
315	char dummy;
316
317	if (d_calls)
318		syslog(LOG_DEBUG, "lock request: %s: %s to %s",
319		    msg->lm_nfsv3 ? "V4" : "V1/3",
320		    msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
321		    from_addr((struct sockaddr_in *)&msg->lm_addr));
322
323	if (msg->lm_nfsv3) {
324		arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
325		arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
326		arg4.block = msg->lm_wait ? 1 : 0;
327		arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
328		arg4.alock.caller_name = hostname;
329		arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
330		arg4.alock.fh.n_len = msg->lm_fh_len;
331		arg4.alock.oh.n_bytes = (char *)&owner;
332		arg4.alock.oh.n_len = sizeof(owner);
333		arg4.alock.svid = msg->lm_msg_ident.pid;
334		arg4.alock.l_offset = msg->lm_fl.l_start;
335		arg4.alock.l_len = msg->lm_fl.l_len;
336		arg4.reclaim = 0;
337		arg4.state = nsm_state;
338
339		if ((cli = get_client(
340		    (struct sockaddr *)&msg->lm_addr,
341		    NLM_VERS4)) == NULL)
342			return (1);
343
344		set_auth(cli, &msg->lm_cred);
345		(void)clnt_call(cli, NLM_LOCK_MSG,
346		    xdr_nlm4_lockargs, &arg4, xdr_void, &dummy, timeout);
347	} else {
348		arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
349		arg.cookie.n_len = sizeof(msg->lm_msg_ident);
350		arg.block = msg->lm_wait ? 1 : 0;
351		arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
352		arg.alock.caller_name = hostname;
353		arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
354		arg.alock.fh.n_len = msg->lm_fh_len;
355		arg.alock.oh.n_bytes = (char *)&owner;
356		arg.alock.oh.n_len = sizeof(owner);
357		arg.alock.svid = msg->lm_msg_ident.pid;
358		arg.alock.l_offset = msg->lm_fl.l_start;
359		arg.alock.l_len = msg->lm_fl.l_len;
360		arg.reclaim = 0;
361		arg.state = nsm_state;
362
363		if ((cli = get_client(
364		    (struct sockaddr *)&msg->lm_addr,
365		    NLM_VERS)) == NULL)
366			return (1);
367
368		set_auth(cli, &msg->lm_cred);
369		(void)clnt_call(cli, NLM_LOCK_MSG,
370		    xdr_nlm_lockargs, &arg, xdr_void, &dummy, timeout);
371	}
372	return (0);
373}
374
375/*
376 * unlock_request --
377 *	Convert an unlock LOCKD_MSG into an NLM request, and send it off.
378 */
379int
380unlock_request(LOCKD_MSG *msg)
381{
382	CLIENT *cli;
383	struct nlm4_unlockargs arg4;
384	struct nlm_unlockargs arg;
385	struct timeval timeout = {0, 0};	/* No timeout, no response. */
386	char dummy;
387
388	if (d_calls)
389		syslog(LOG_DEBUG, "unlock request: %s: to %s",
390		    msg->lm_nfsv3 ? "V4" : "V1/3",
391		    from_addr((struct sockaddr_in *)&msg->lm_addr));
392
393	if (msg->lm_nfsv3) {
394		arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
395		arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
396		arg4.alock.caller_name = hostname;
397		arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
398		arg4.alock.fh.n_len = msg->lm_fh_len;
399		arg4.alock.oh.n_bytes = (char *)&owner;
400		arg4.alock.oh.n_len = sizeof(owner);
401		arg4.alock.svid = msg->lm_msg_ident.pid;
402		arg4.alock.l_offset = msg->lm_fl.l_start;
403		arg4.alock.l_len = msg->lm_fl.l_len;
404
405		if ((cli = get_client(
406		    (struct sockaddr *)&msg->lm_addr,
407		    NLM_VERS4)) == NULL)
408			return (1);
409
410		set_auth(cli, &msg->lm_cred);
411		(void)clnt_call(cli, NLM_UNLOCK_MSG,
412		    xdr_nlm4_unlockargs, &arg4, xdr_void, &dummy, timeout);
413	} else {
414		arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
415		arg.cookie.n_len = sizeof(msg->lm_msg_ident);
416		arg.alock.caller_name = hostname;
417		arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
418		arg.alock.fh.n_len = msg->lm_fh_len;
419		arg.alock.oh.n_bytes = (char *)&owner;
420		arg.alock.oh.n_len = sizeof(owner);
421		arg.alock.svid = msg->lm_msg_ident.pid;
422		arg.alock.l_offset = msg->lm_fl.l_start;
423		arg.alock.l_len = msg->lm_fl.l_len;
424
425		if ((cli = get_client(
426		    (struct sockaddr *)&msg->lm_addr,
427		    NLM_VERS)) == NULL)
428			return (1);
429
430		set_auth(cli, &msg->lm_cred);
431		(void)clnt_call(cli, NLM_UNLOCK_MSG,
432		    xdr_nlm_unlockargs, &arg, xdr_void, &dummy, timeout);
433	}
434
435	return (0);
436}
437
438int
439lock_answer(int pid, netobj *netcookie, int result, int *pid_p, int version)
440{
441	struct lockd_ans ans;
442
443	if (netcookie->n_len != sizeof(ans.la_msg_ident)) {
444		if (pid == -1) {	/* we're screwed */
445			syslog(LOG_ERR, "inedible nlm cookie");
446			return -1;
447		}
448		ans.la_msg_ident.pid = pid;
449		ans.la_msg_ident.msg_seq = -1;
450	} else {
451		memcpy(&ans.la_msg_ident, netcookie->n_bytes,
452		    sizeof(ans.la_msg_ident));
453	}
454
455	if (d_calls)
456		syslog(LOG_DEBUG, "lock answer: pid %lu: %s %d",
457		    ans.la_msg_ident.pid,
458		    version == NLM_VERS4 ? "nlmv4" : "nlmv3",
459		    result);
460
461	ans.la_set_getlk_pid = 0;
462	if (version == NLM_VERS4)
463		switch (result) {
464		case nlm4_granted:
465			ans.la_errno = 0;
466			break;
467		default:
468			ans.la_errno = EACCES;
469			break;
470		case nlm4_denied:
471			if (pid_p == NULL)
472				ans.la_errno = EACCES;
473			else {
474				/* this is an answer to a nlm_test msg */
475				ans.la_set_getlk_pid = 1;
476				ans.la_getlk_pid = *pid_p;
477				ans.la_errno = 0;
478			}
479			break;
480		case nlm4_denied_nolocks:
481			ans.la_errno = EAGAIN;
482			break;
483		case nlm4_blocked:
484			return -1;
485			/* NOTREACHED */
486		case nlm4_denied_grace_period:
487			ans.la_errno = EAGAIN;
488			break;
489		case nlm4_deadlck:
490			ans.la_errno = EDEADLK;
491			break;
492		case nlm4_rofs:
493			ans.la_errno = EROFS;
494			break;
495		case nlm4_stale_fh:
496			ans.la_errno = ESTALE;
497			break;
498		case nlm4_fbig:
499			ans.la_errno = EFBIG;
500			break;
501		case nlm4_failed:
502			ans.la_errno = EACCES;
503			break;
504		}
505	else
506		switch (result) {
507		case nlm_granted:
508			ans.la_errno = 0;
509			break;
510		default:
511			ans.la_errno = EACCES;
512			break;
513		case nlm_denied:
514			if (pid_p == NULL)
515				ans.la_errno = EACCES;
516			else {
517				/* this is an answer to a nlm_test msg */
518				ans.la_set_getlk_pid = 1;
519				ans.la_getlk_pid = *pid_p;
520				ans.la_errno = 0;
521			}
522			break;
523		case nlm_denied_nolocks:
524			ans.la_errno = EAGAIN;
525			break;
526		case nlm_blocked:
527			return -1;
528			/* NOTREACHED */
529		case nlm_denied_grace_period:
530			ans.la_errno = EAGAIN;
531			break;
532		case nlm_deadlck:
533			ans.la_errno = EDEADLK;
534			break;
535		}
536
537	if (nfslockdans(LOCKD_ANS_VERSION, &ans)) {
538		syslog(((errno == EPIPE || errno == ESRCH) ?
539			LOG_INFO : LOG_ERR),
540			"process %lu: %m", (u_long)ans.la_msg_ident.pid);
541		return -1;
542	}
543	return 0;
544}
545
546/*
547 * show --
548 *	Display the contents of a kernel LOCKD_MSG structure.
549 */
550void
551show(LOCKD_MSG *mp)
552{
553	static char hex[] = "0123456789abcdef";
554	struct fid *fidp;
555	fsid_t *fsidp;
556	size_t len;
557	u_int8_t *p, *t, buf[NFS_SMALLFH*3+1];
558
559	syslog(LOG_DEBUG, "process ID: %lu\n", (long)mp->lm_msg_ident.pid);
560
561	fsidp = (fsid_t *)&mp->lm_fh;
562	fidp = (struct fid *)((u_int8_t *)&mp->lm_fh + sizeof(fsid_t));
563
564	for (t = buf, p = (u_int8_t *)mp->lm_fh,
565	    len = mp->lm_fh_len;
566	    len > 0; ++p, --len) {
567		*t++ = '\\';
568		*t++ = hex[(*p & 0xf0) >> 4];
569		*t++ = hex[*p & 0x0f];
570	}
571	*t = '\0';
572
573	syslog(LOG_DEBUG, "fh_len %d, fh %s\n", mp->lm_fh_len, buf);
574
575	/* Show flock structure. */
576	syslog(LOG_DEBUG, "start %qu; len %qu; pid %lu; type %d; whence %d\n",
577	    mp->lm_fl.l_start, mp->lm_fl.l_len, (u_long)mp->lm_fl.l_pid,
578	    mp->lm_fl.l_type, mp->lm_fl.l_whence);
579
580	/* Show wait flag. */
581	syslog(LOG_DEBUG, "wait was %s\n", mp->lm_wait ? "set" : "not set");
582}
583