PageRenderTime 20ms CodeModel.GetById 1ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/pdsh/privsep.c

https://code.google.com/
C | 338 lines | 229 code | 63 blank | 46 comment | 29 complexity | b5adc6fef3d4874507c14338fba5dfa5 MD5 | raw file
  1/*****************************************************************************\
  2 *  $Id$
  3 *****************************************************************************
  4 *  Copyright (C) 2001-2006 The Regents of the University of California.
  5 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  6 *  Written by Jim Garlick <garlick@llnl.gov>.
  7 *  UCRL-CODE-2003-005.
  8 *  
  9 *  This file is part of Pdsh, a parallel remote shell program.
 10 *  For details, see <http://www.llnl.gov/linux/pdsh/>.
 11 *  
 12 *  Pdsh is free software; you can redistribute it and/or modify it under
 13 *  the terms of the GNU General Public License as published by the Free
 14 *  Software Foundation; either version 2 of the License, or (at your option)
 15 *  any later version.
 16 *  
 17 *  Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY
 18 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 19 *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 20 *  details.
 21 *  
 22 *  You should have received a copy of the GNU General Public License along
 23 *  with Pdsh; if not, write to the Free Software Foundation, Inc.,
 24 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 25\*****************************************************************************/
 26
 27#if HAVE_CONFIG_H
 28#  include <config.h>
 29#endif
 30
 31#include <sys/types.h>
 32#if HAVE_SYS_UIO_H
 33#  include <sys/uio.h>
 34#endif
 35
 36#include <sys/socket.h>
 37#include <sys/wait.h>
 38#include <unistd.h>
 39#include <pthread.h>
 40#include <stdlib.h>
 41#include <netdb.h>  /* rresvport */
 42#include <assert.h>
 43#include <string.h>
 44#include <errno.h>
 45
 46#include "src/common/err.h"
 47#include "src/common/fd.h"
 48
 49#define CONTROLLEN sizeof (struct cmsghdr) + sizeof (int)
 50
 51static pthread_mutex_t privsep_mutex = PTHREAD_MUTEX_INITIALIZER;
 52static pid_t cpid;
 53static int client_fd = -1;
 54static int server_fd = -1;
 55
 56uid_t user_uid = -1;
 57gid_t user_gid = -1;
 58uid_t priv_uid = -1;
 59gid_t priv_gid = -1;
 60
 61/*
 62 *  Top 16 bits of port sent to privsep server is used to encode
 63 *   the address family (for rresvport_af()). Currently, value is
 64 *   either AF_INET AF_INET6 (if zero, then AF_INET).
 65 */
 66static int privsep_get_family (int *lport)
 67{
 68	int family = (*lport>>16) & 0xffff;
 69	/*
 70	 *  Mask out family from lport:
 71	 */
 72	*lport &= 0xffff;
 73
 74	return (family ? family : AF_INET);
 75}
 76
 77static int privsep_set_family (int *lport, int family)
 78{
 79	if (family > 0xffff)
 80		return (-1);
 81	*lport |= (family<<16);
 82	return (0);
 83}
 84
 85static int create_socketpair (void)
 86{
 87	int pfds[2];
 88
 89	if (socketpair (AF_UNIX, SOCK_STREAM, 0, pfds) < 0) {
 90		err ("%p: socketpair failed in privilege separation: %m\n");
 91		return -1;
 92	}
 93
 94	client_fd = pfds[0];
 95	server_fd = pfds[1];
 96
 97	return (0);
 98}
 99
100static void drop_privileges (void)
101{
102	user_uid = getuid ();
103	priv_uid = geteuid ();
104	user_gid = getgid ();
105	priv_gid = getegid ();
106
107#ifdef _POSIX_SAVED_IDS
108	seteuid (user_uid);
109	setegid (user_gid);
110#else
111	setreuid (priv_uid, user_uid);
112	setregid (priv_gid, user_gid);
113#endif
114}
115
116static int send_rresvport (int pipefd, int fd, int lport)
117{
118	struct iovec   iov[1];
119	struct msghdr  msg;
120#if !HAVE_MSGHDR_ACCRIGHTS
121	struct cmsghdr *cmsg;
122	char *         buf[CONTROLLEN];
123
124	cmsg = (struct cmsghdr *) &buf;
125#endif
126
127	memset (&msg, 0, sizeof (msg));
128
129	iov->iov_base  = (void *) &lport;
130	iov->iov_len   = sizeof (lport);
131	msg.msg_iov    = iov;
132	msg.msg_iovlen = 1;
133
134#if HAVE_MSGHDR_ACCRIGHTS
135	if (fd < 0) {
136		msg.msg_accrights = NULL;
137		msg.msg_accrightslen = 0;
138	} else {
139		msg.msg_accrights = (caddr_t) &fd;
140		msg.msg_accrightslen = sizeof (int);
141	}
142#else 
143	if (fd < 0) {
144		msg.msg_control = NULL;
145		msg.msg_controllen = 0;
146		lport = -1;
147	} else {
148		cmsg->cmsg_level   = SOL_SOCKET;
149		cmsg->cmsg_type    = SCM_RIGHTS;
150		cmsg->cmsg_len     = CONTROLLEN;
151		msg.msg_control    = (caddr_t) cmsg;
152		msg.msg_controllen = CONTROLLEN;
153		* (int *) CMSG_DATA(cmsg) = fd;
154	}
155#endif
156
157	if (sendmsg (pipefd, &msg, 0) != sizeof (int)) {
158		err ("%p: privsep: sendmsg: %m\n");
159		return (-1);
160	}
161
162	return (0);
163}
164
165static int recv_rresvport (int pipefd, int *lptr)
166{
167	int            fd = -1;
168	struct iovec   iov[1];
169	struct msghdr  msg;
170#if !HAVE_MSGHDR_ACCRIGHTS
171	struct cmsghdr *cmsg;
172	char *         buf[CONTROLLEN];
173
174	cmsg = (struct cmsghdr *) &buf;
175#endif
176	memset (&msg, 0, sizeof (msg));
177
178	iov->iov_base  = (void *) lptr;
179	iov->iov_len   = sizeof (int);
180	msg.msg_iov    = iov;
181	msg.msg_iovlen = 1;
182
183#if HAVE_MSGHDR_ACCRIGHTS
184	msg.msg_accrights = (caddr_t) &fd;
185	msg.msg_accrightslen = sizeof (int);
186#else /* !HAVE_MSGHDR_ACCRIGHTS */
187	cmsg->cmsg_level   = SOL_SOCKET;
188	cmsg->cmsg_type    = SCM_RIGHTS;
189	cmsg->cmsg_len     = CONTROLLEN;
190	msg.msg_control    = (caddr_t) cmsg;
191	msg.msg_controllen = CONTROLLEN;
192#endif
193
194	if (recvmsg (pipefd, &msg, 0) < 0)
195		err ("%p: privsep: recvmsg: %m\n");
196
197	if (*lptr < 0)
198		return (-1);
199
200#if !HAVE_MSGHDR_ACCRIGHTS
201	fd = *(int *) CMSG_DATA (cmsg);
202#endif
203
204	return (fd);
205}
206
207
208static int p_rresvport_af (int *port, int family)
209{
210#if HAVE_RRESVPORT_AF
211	return rresvport_af (port, family);
212#else
213	/*  Family must be AF_INET
214	 */
215	if (family != AF_INET)
216		err ("%p: rresvport called with family != AF_INET!\n");
217	/* ignore family != AF_INET */
218	return rresvport (port);
219#endif
220}
221
222static int privsep_server (void)
223{
224	int rc;
225	int lport;
226	close (client_fd);
227
228	/*
229	 * for each request on server_fd create a reserved port and
230	 *   send the created fd back to the client.
231	 */
232	while ((rc = read (server_fd, &lport, sizeof (lport))) > 0) {
233		int family = privsep_get_family (&lport);
234		int s = p_rresvport_af (&lport, family);
235
236		send_rresvport (server_fd, s, lport);
237
238		close (s);
239	}
240
241	if (rc < 0)
242		err ("%p: privsep: server read failed: %m\n");
243
244	close (server_fd);
245
246	return (0);
247
248}
249
250static int create_privileged_child (void)
251{
252	if ((cpid = fork ()) < 0) {
253		err ("%p: fork failed in privilege separation: %m\n");
254		return -1;
255	}
256
257	if (cpid == 0) {
258		/* 
259		 * Child: become privilege port server.
260		 */
261		privsep_server ();
262		exit (0);
263	}
264
265	/*
266	 * Parent: drop privileges, close server_fd and return.
267	 */
268	close (server_fd);
269
270	drop_privileges ();
271
272	return (0);
273}
274
275int privsep_init (void)
276{
277	if (geteuid() == getuid())
278		return 0;
279
280	if (create_socketpair () < 0)
281		return -1;
282
283	return (create_privileged_child ());
284}
285
286int privsep_fini (void)
287{
288	int status;
289	if (client_fd < 0 || cpid < 0)
290		return (0);
291
292	close (client_fd);
293
294	if (waitpid (cpid, &status, 0) < 0) {
295		err ("%p: failed to reap priveleged child: %m\n");
296		return (-1);
297	}
298
299	if (status) 
300		err ("%p: privileged chiled exited with status %d\n", status);
301
302	return (0);
303}
304
305int privsep_rresvport_af (int *lport, int family)
306{
307	int s = -1;
308
309	if (client_fd < 0)
310		return (p_rresvport_af (lport, family));
311
312	if (privsep_set_family (lport, family) < 0) {
313		err ("%p: privsep_rresvport_af: Invalid family %d\n", family);
314		errno = EINVAL;
315		return (-1);
316	}
317
318	if ((errno = pthread_mutex_lock (&privsep_mutex)))
319		errx ("%p: %s:%d: mutex_lock: %m\n", __FILE__, __LINE__);
320
321	if (write (client_fd, lport, sizeof (*lport)) < 0) {
322		err ("%p: privsep: client write: %m\n");
323		goto out;
324	}
325
326	s = recv_rresvport (client_fd, lport);
327
328out:
329	if ((errno = pthread_mutex_unlock (&privsep_mutex)))
330		errx ("%p: %s:%d: mutex_unlock: %m\n", __FILE__, __LINE__);
331
332	return (s);
333}
334
335int privsep_rresvport (int *lport)
336{
337	return privsep_rresvport_af (lport, AF_INET);
338}