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