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