1/*	$NetBSD: getpeereid.c,v 1.1.1.4 2010/12/12 15:22:08 adam Exp $	*/
2
3/* getpeereid.c */
4/* OpenLDAP: pkg/ldap/libraries/liblutil/getpeereid.c,v 1.24.2.7 2010/04/13 20:23:05 kurt Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2000-2010 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18
19#ifndef _GNU_SOURCE
20#define _GNU_SOURCE 1			/* Needed for glibc struct ucred */
21#endif
22
23#include "portable.h"
24
25#ifndef HAVE_GETPEEREID
26
27#include <sys/types.h>
28#include <ac/unistd.h>
29
30#include <ac/socket.h>
31#include <ac/errno.h>
32
33#ifdef HAVE_GETPEERUCRED
34#include <ucred.h>
35#endif
36
37#ifdef LDAP_PF_LOCAL_SENDMSG
38#include <lber.h>
39#ifdef HAVE_SYS_UIO_H
40#include <sys/uio.h>
41#endif
42#include <sys/stat.h>
43#endif
44
45#ifdef HAVE_SYS_UCRED_H
46#ifdef HAVE_GRP_H
47#include <grp.h>	/* for NGROUPS on Tru64 5.1 */
48#endif
49#include <sys/ucred.h>
50#endif
51
52#include <stdlib.h>
53
54int lutil_getpeereid( int s, uid_t *euid, gid_t *egid
55#ifdef LDAP_PF_LOCAL_SENDMSG
56	, struct berval *peerbv
57#endif
58	)
59{
60#ifdef LDAP_PF_LOCAL
61#if defined( HAVE_GETPEERUCRED )
62	ucred_t *uc = NULL;
63	if( getpeerucred( s, &uc ) == 0 )  {
64		*euid = ucred_geteuid( uc );
65		*egid = ucred_getegid( uc );
66		ucred_free( uc );
67		return 0;
68	}
69
70#elif defined( SO_PEERCRED )
71	struct ucred peercred;
72	ber_socklen_t peercredlen = sizeof peercred;
73
74	if(( getsockopt( s, SOL_SOCKET, SO_PEERCRED,
75		(void *)&peercred, &peercredlen ) == 0 )
76		&& ( peercredlen == sizeof peercred ))
77	{
78		*euid = peercred.uid;
79		*egid = peercred.gid;
80		return 0;
81	}
82
83#elif defined( LOCAL_PEERCRED )
84	struct xucred peercred;
85	ber_socklen_t peercredlen = sizeof peercred;
86
87	if(( getsockopt( s, LOCAL_PEERCRED, 1,
88		(void *)&peercred, &peercredlen ) == 0 )
89		&& ( peercred.cr_version == XUCRED_VERSION ))
90	{
91		*euid = peercred.cr_uid;
92		*egid = peercred.cr_gid;
93		return 0;
94	}
95#elif defined( LDAP_PF_LOCAL_SENDMSG ) && defined( MSG_WAITALL )
96	int err, fd;
97	struct iovec iov;
98	struct msghdr msg = {0};
99# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
100# ifndef CMSG_SPACE
101# define CMSG_SPACE(len)	(_CMSG_ALIGN(sizeof(struct cmsghdr)) + _CMSG_ALIGN(len))
102# endif
103# ifndef CMSG_LEN
104# define CMSG_LEN(len)		(_CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
105# endif
106	struct {
107		struct cmsghdr cm;
108		int fd;
109	} control_st;
110	struct cmsghdr *cmsg;
111# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
112	struct stat st;
113	struct sockaddr_un lname, rname;
114	ber_socklen_t llen, rlen;
115
116	rlen = sizeof(rname);
117	llen = sizeof(lname);
118	memset( &lname, 0, sizeof( lname ));
119	getsockname(s, (struct sockaddr *)&lname, &llen);
120
121	iov.iov_base = peerbv->bv_val;
122	iov.iov_len = peerbv->bv_len;
123	msg.msg_iov = &iov;
124	msg.msg_iovlen = 1;
125	peerbv->bv_len = 0;
126
127# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
128	msg.msg_control = &control_st;
129	msg.msg_controllen = sizeof( struct cmsghdr ) + sizeof( int );	/* no padding! */
130
131	cmsg = CMSG_FIRSTHDR( &msg );
132# else
133	msg.msg_accrights = (char *)&fd;
134	msg.msg_accrightslen = sizeof(fd);
135# endif
136
137	/*
138	 * AIX returns a bogus file descriptor if recvmsg() is
139	 * called with MSG_PEEK (is this a bug?). Hence we need
140	 * to receive the Abandon PDU.
141	 */
142	err = recvmsg( s, &msg, MSG_WAITALL );
143	if( err >= 0 &&
144# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
145	    cmsg->cmsg_len == CMSG_LEN( sizeof(int) ) &&
146	    cmsg->cmsg_level == SOL_SOCKET &&
147	    cmsg->cmsg_type == SCM_RIGHTS
148# else
149		msg.msg_accrightslen == sizeof(int)
150# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL*/
151	) {
152		int mode = S_IFIFO|S_ISUID|S_IRWXU;
153
154		/* We must receive a valid descriptor, it must be a pipe,
155		 * it must only be accessible by its owner, and it must
156		 * have the name of our socket written on it.
157		 */
158		peerbv->bv_len = err;
159# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
160		fd = (*(int *)CMSG_DATA( cmsg ));
161# endif
162		err = fstat( fd, &st );
163		if ( err == 0 )
164			rlen = read(fd, &rname, rlen);
165		close(fd);
166		if( err == 0 && st.st_mode == mode &&
167			llen == rlen && !memcmp(&lname, &rname, llen))
168		{
169			*euid = st.st_uid;
170			*egid = st.st_gid;
171			return 0;
172		}
173	}
174#elif defined(SOCKCREDSIZE)
175	struct msghdr msg;
176	ber_socklen_t crmsgsize;
177	void *crmsg;
178	struct cmsghdr *cmp;
179	struct sockcred *sc;
180
181	memset(&msg, 0, sizeof msg);
182	crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS));
183	if (crmsgsize == 0) goto sc_err;
184	crmsg = malloc(crmsgsize);
185	if (crmsg == NULL) goto sc_err;
186	memset(crmsg, 0, crmsgsize);
187
188	msg.msg_control = crmsg;
189	msg.msg_controllen = crmsgsize;
190
191	if (recvmsg(s, &msg, 0) < 0) {
192		free(crmsg);
193		goto sc_err;
194	}
195
196	if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) {
197		free(crmsg);
198		goto sc_err;
199	}
200
201	cmp = CMSG_FIRSTHDR(&msg);
202	if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) {
203		printf("nocreds\n");
204		goto sc_err;
205	}
206
207	sc = (struct sockcred *)(void *)CMSG_DATA(cmp);
208
209	*euid = sc->sc_euid;
210	*egid = sc->sc_egid;
211
212	free(crmsg);
213	return 0;
214
215sc_err:
216#endif
217#endif /* LDAP_PF_LOCAL */
218
219	return -1;
220}
221
222#endif /* HAVE_GETPEEREID */
223