]> git.sur5r.net Git - openldap/blob - libraries/liblutil/getpeereid.c
Happy New Year!
[openldap] / libraries / liblutil / getpeereid.c
1 /* getpeereid.c */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2016 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
52 int 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
213 sc_err: 
214 #endif
215 #endif /* LDAP_PF_LOCAL */
216
217         return -1;
218 }
219
220 #endif /* HAVE_GETPEEREID */