summaryrefslogtreecommitdiffstats
path: root/libraries/liblutil/getpeereid.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/liblutil/getpeereid.c')
-rw-r--r--libraries/liblutil/getpeereid.c220
1 files changed, 220 insertions, 0 deletions
diff --git a/libraries/liblutil/getpeereid.c b/libraries/liblutil/getpeereid.c
new file mode 100644
index 0000000..056caef
--- /dev/null
+++ b/libraries/liblutil/getpeereid.c
@@ -0,0 +1,220 @@
+/* getpeereid.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2018 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1 /* Needed for glibc struct ucred */
+#endif
+
+#include "portable.h"
+
+#ifndef HAVE_GETPEEREID
+
+#include <sys/types.h>
+#include <ac/unistd.h>
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+
+#ifdef HAVE_GETPEERUCRED
+#include <ucred.h>
+#endif
+
+#ifdef LDAP_PF_LOCAL_SENDMSG
+#include <lber.h>
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_UCRED_H
+#ifdef HAVE_GRP_H
+#include <grp.h> /* for NGROUPS on Tru64 5.1 */
+#endif
+#include <sys/ucred.h>
+#endif
+
+#include <stdlib.h>
+
+int lutil_getpeereid( int s, uid_t *euid, gid_t *egid
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ , struct berval *peerbv
+#endif
+ )
+{
+#ifdef LDAP_PF_LOCAL
+#if defined( HAVE_GETPEERUCRED )
+ ucred_t *uc = NULL;
+ if( getpeerucred( s, &uc ) == 0 ) {
+ *euid = ucred_geteuid( uc );
+ *egid = ucred_getegid( uc );
+ ucred_free( uc );
+ return 0;
+ }
+
+#elif defined( SO_PEERCRED )
+ struct ucred peercred;
+ ber_socklen_t peercredlen = sizeof peercred;
+
+ if(( getsockopt( s, SOL_SOCKET, SO_PEERCRED,
+ (void *)&peercred, &peercredlen ) == 0 )
+ && ( peercredlen == sizeof peercred ))
+ {
+ *euid = peercred.uid;
+ *egid = peercred.gid;
+ return 0;
+ }
+
+#elif defined( LOCAL_PEERCRED )
+ struct xucred peercred;
+ ber_socklen_t peercredlen = sizeof peercred;
+
+ if(( getsockopt( s, LOCAL_PEERCRED, 1,
+ (void *)&peercred, &peercredlen ) == 0 )
+ && ( peercred.cr_version == XUCRED_VERSION ))
+ {
+ *euid = peercred.cr_uid;
+ *egid = peercred.cr_gid;
+ return 0;
+ }
+#elif defined( LDAP_PF_LOCAL_SENDMSG ) && defined( MSG_WAITALL )
+ int err, fd;
+ struct iovec iov;
+ struct msghdr msg = {0};
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+# ifndef CMSG_SPACE
+# define CMSG_SPACE(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + _CMSG_ALIGN(len))
+# endif
+# ifndef CMSG_LEN
+# define CMSG_LEN(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
+# endif
+ struct {
+ struct cmsghdr cm;
+ int fd;
+ } control_st;
+ struct cmsghdr *cmsg;
+# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
+ struct stat st;
+ struct sockaddr_un lname, rname;
+ ber_socklen_t llen, rlen;
+
+ rlen = sizeof(rname);
+ llen = sizeof(lname);
+ memset( &lname, 0, sizeof( lname ));
+ getsockname(s, (struct sockaddr *)&lname, &llen);
+
+ iov.iov_base = peerbv->bv_val;
+ iov.iov_len = peerbv->bv_len;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ peerbv->bv_len = 0;
+
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ msg.msg_control = &control_st;
+ msg.msg_controllen = sizeof( struct cmsghdr ) + sizeof( int ); /* no padding! */
+
+ cmsg = CMSG_FIRSTHDR( &msg );
+# else
+ msg.msg_accrights = (char *)&fd;
+ msg.msg_accrightslen = sizeof(fd);
+# endif
+
+ /*
+ * AIX returns a bogus file descriptor if recvmsg() is
+ * called with MSG_PEEK (is this a bug?). Hence we need
+ * to receive the Abandon PDU.
+ */
+ err = recvmsg( s, &msg, MSG_WAITALL );
+ if( err >= 0 &&
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ cmsg->cmsg_len == CMSG_LEN( sizeof(int) ) &&
+ cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS
+# else
+ msg.msg_accrightslen == sizeof(int)
+# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL*/
+ ) {
+ int mode = S_IFIFO|S_ISUID|S_IRWXU;
+
+ /* We must receive a valid descriptor, it must be a pipe,
+ * it must only be accessible by its owner, and it must
+ * have the name of our socket written on it.
+ */
+ peerbv->bv_len = err;
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ fd = (*(int *)CMSG_DATA( cmsg ));
+# endif
+ err = fstat( fd, &st );
+ if ( err == 0 )
+ rlen = read(fd, &rname, rlen);
+ close(fd);
+ if( err == 0 && st.st_mode == mode &&
+ llen == rlen && !memcmp(&lname, &rname, llen))
+ {
+ *euid = st.st_uid;
+ *egid = st.st_gid;
+ return 0;
+ }
+ }
+#elif defined(SOCKCREDSIZE)
+ struct msghdr msg;
+ ber_socklen_t crmsgsize;
+ void *crmsg;
+ struct cmsghdr *cmp;
+ struct sockcred *sc;
+
+ memset(&msg, 0, sizeof msg);
+ crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS));
+ if (crmsgsize == 0) goto sc_err;
+ crmsg = malloc(crmsgsize);
+ if (crmsg == NULL) goto sc_err;
+ memset(crmsg, 0, crmsgsize);
+
+ msg.msg_control = crmsg;
+ msg.msg_controllen = crmsgsize;
+
+ if (recvmsg(s, &msg, 0) < 0) {
+ free(crmsg);
+ goto sc_err;
+ }
+
+ if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) {
+ free(crmsg);
+ goto sc_err;
+ }
+
+ cmp = CMSG_FIRSTHDR(&msg);
+ if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) {
+ printf("nocreds\n");
+ goto sc_err;
+ }
+
+ sc = (struct sockcred *)(void *)CMSG_DATA(cmp);
+
+ *euid = sc->sc_euid;
+ *egid = sc->sc_egid;
+
+ free(crmsg);
+ return 0;
+
+sc_err:
+#endif
+#endif /* LDAP_PF_LOCAL */
+
+ return -1;
+}
+
+#endif /* HAVE_GETPEEREID */