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