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