1 /* $NetBSD: os-local.c,v 1.11 2020/08/11 13:15:37 christos Exp $ */ 2 3 /* os-local.c -- platform-specific domain socket code */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 1998-2020 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 /* Portions Copyright (c) 1995 Regents of the University of Michigan. 19 * All rights reserved. 20 */ 21 /* Portions (C) Copyright PADL Software Pty Ltd. 1999 22 * Redistribution and use in source and binary forms, with or without 23 * modification, are permitted provided that this notice is preserved 24 * and that due credit is given to PADL Software Pty Ltd. This software 25 * is provided ``as is'' without express or implied warranty. 26 */ 27 28 #include <sys/cdefs.h> 29 __RCSID("$NetBSD: os-local.c,v 1.11 2020/08/11 13:15:37 christos Exp $"); 30 31 #include "portable.h" 32 33 #ifdef LDAP_PF_LOCAL 34 35 #include <stdio.h> 36 37 #include <ac/stdlib.h> 38 39 #include <ac/errno.h> 40 #include <ac/socket.h> 41 #include <ac/string.h> 42 #include <ac/time.h> 43 #include <ac/unistd.h> 44 45 #ifdef HAVE_SYS_STAT_H 46 #include <sys/stat.h> 47 #endif 48 #ifdef HAVE_SYS_UIO_H 49 #include <sys/uio.h> 50 #endif 51 52 #ifdef HAVE_IO_H 53 #include <io.h> 54 #endif /* HAVE_IO_H */ 55 #ifdef HAVE_FCNTL_H 56 #include <fcntl.h> 57 #endif 58 59 #include "ldap-int.h" 60 #include "ldap_defaults.h" 61 62 #ifdef LDAP_DEBUG 63 64 #define oslocal_debug(ld,fmt,arg1,arg2,arg3) \ 65 do { \ 66 ldap_log_printf(ld, LDAP_DEBUG_TRACE, fmt, arg1, arg2, arg3); \ 67 } while(0) 68 69 #else 70 71 #define oslocal_debug(ld,fmt,arg1,arg2,arg3) ((void)0) 72 73 #endif /* LDAP_DEBUG */ 74 75 static void 76 ldap_pvt_set_errno(int err) 77 { 78 errno = err; 79 } 80 81 static int 82 ldap_pvt_ndelay_on(LDAP *ld, int fd) 83 { 84 oslocal_debug(ld, "ldap_ndelay_on: %d\n",fd,0,0); 85 return ber_pvt_socket_set_nonblock( fd, 1 ); 86 } 87 88 static int 89 ldap_pvt_ndelay_off(LDAP *ld, int fd) 90 { 91 oslocal_debug(ld, "ldap_ndelay_off: %d\n",fd,0,0); 92 return ber_pvt_socket_set_nonblock( fd, 0 ); 93 } 94 95 static ber_socket_t 96 ldap_pvt_socket(LDAP *ld) 97 { 98 ber_socket_t s = socket(PF_LOCAL, SOCK_STREAM, 0); 99 oslocal_debug(ld, "ldap_new_socket: %d\n",s,0,0); 100 #ifdef FD_CLOEXEC 101 fcntl(s, F_SETFD, FD_CLOEXEC); 102 #endif 103 return ( s ); 104 } 105 106 static int 107 ldap_pvt_close_socket(LDAP *ld, int s) 108 { 109 oslocal_debug(ld, "ldap_close_socket: %d\n",s,0,0); 110 return tcp_close(s); 111 } 112 113 #undef TRACE 114 #define TRACE do { \ 115 char ebuf[128]; \ 116 oslocal_debug(ld, \ 117 "ldap_is_socket_ready: error on socket %d: errno: %d (%s)\n", \ 118 s, \ 119 errno, \ 120 AC_STRERROR_R(errno, ebuf, sizeof ebuf)); \ 121 } while( 0 ) 122 123 /* 124 * check the socket for errors after select returned. 125 */ 126 static int 127 ldap_pvt_is_socket_ready(LDAP *ld, int s) 128 { 129 oslocal_debug(ld, "ldap_is_sock_ready: %d\n",s,0,0); 130 131 #if defined( notyet ) /* && defined( SO_ERROR ) */ 132 { 133 int so_errno; 134 ber_socklen_t dummy = sizeof(so_errno); 135 if ( getsockopt( s, SOL_SOCKET, SO_ERROR, &so_errno, &dummy ) 136 == AC_SOCKET_ERROR ) 137 { 138 return -1; 139 } 140 if ( so_errno ) { 141 ldap_pvt_set_errno(so_errno); 142 TRACE; 143 return -1; 144 } 145 return 0; 146 } 147 #else 148 { 149 /* error slippery */ 150 struct sockaddr_un sa; 151 char ch; 152 ber_socklen_t dummy = sizeof(sa); 153 if ( getpeername( s, (struct sockaddr *) &sa, &dummy ) 154 == AC_SOCKET_ERROR ) 155 { 156 /* XXX: needs to be replace with ber_stream_read() */ 157 (void)read(s, &ch, 1); 158 TRACE; 159 return -1; 160 } 161 return 0; 162 } 163 #endif 164 return -1; 165 } 166 #undef TRACE 167 168 #ifdef LDAP_PF_LOCAL_SENDMSG 169 static const char abandonPDU[] = {LDAP_TAG_MESSAGE, 6, 170 LDAP_TAG_MSGID, 1, 0, LDAP_REQ_ABANDON, 1, 0}; 171 #endif 172 173 static int 174 ldap_pvt_connect(LDAP *ld, ber_socket_t s, struct sockaddr_un *sa, int async) 175 { 176 int rc; 177 struct timeval tv, *opt_tv = NULL; 178 179 if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) { 180 tv = ld->ld_options.ldo_tm_net; 181 opt_tv = &tv; 182 } 183 184 oslocal_debug(ld, "ldap_connect_timeout: fd: %d tm: %jd async: %d\n", 185 s, opt_tv ? (intmax_t)tv.tv_sec : -1, async); 186 187 if ( ldap_pvt_ndelay_on(ld, s) == -1 ) return -1; 188 189 if ( connect(s, (struct sockaddr *) sa, sizeof(struct sockaddr_un)) 190 != AC_SOCKET_ERROR ) 191 { 192 if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1; 193 194 #ifdef LDAP_PF_LOCAL_SENDMSG 195 /* Send a dummy message with access rights. Remote side will 196 * obtain our uid/gid by fstat'ing this descriptor. The 197 * descriptor permissions must match exactly, and we also 198 * send the socket name, which must also match. 199 */ 200 sendcred: 201 { 202 int fds[2]; 203 ber_socklen_t salen = sizeof(*sa); 204 if (pipe(fds) == 0) { 205 /* Abandon, noop, has no reply */ 206 struct iovec iov; 207 struct msghdr msg = {0}; 208 # ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL 209 # ifndef CMSG_SPACE 210 # define CMSG_SPACE(len) (_CMSG_ALIGN( sizeof(struct cmsghdr)) + _CMSG_ALIGN(len) ) 211 # endif 212 # ifndef CMSG_LEN 213 # define CMSG_LEN(len) (_CMSG_ALIGN( sizeof(struct cmsghdr)) + (len) ) 214 # endif 215 union { 216 struct cmsghdr cm; 217 unsigned char control[CMSG_SPACE(sizeof(int))]; 218 } control_un; 219 struct cmsghdr *cmsg; 220 # endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ 221 msg.msg_name = NULL; 222 msg.msg_namelen = 0; 223 iov.iov_base = (char *) abandonPDU; 224 iov.iov_len = sizeof abandonPDU; 225 msg.msg_iov = &iov; 226 msg.msg_iovlen = 1; 227 # ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL 228 msg.msg_control = control_un.control; 229 msg.msg_controllen = sizeof( control_un.control ); 230 msg.msg_flags = 0; 231 232 cmsg = CMSG_FIRSTHDR( &msg ); 233 cmsg->cmsg_len = CMSG_LEN( sizeof(int) ); 234 cmsg->cmsg_level = SOL_SOCKET; 235 cmsg->cmsg_type = SCM_RIGHTS; 236 237 *((int *)CMSG_DATA(cmsg)) = fds[0]; 238 # else 239 msg.msg_accrights = (char *)fds; 240 msg.msg_accrightslen = sizeof(int); 241 # endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ 242 getpeername( s, (struct sockaddr *) sa, &salen ); 243 fchmod( fds[0], S_ISUID|S_IRWXU ); 244 write( fds[1], sa, salen ); 245 sendmsg( s, &msg, 0 ); 246 close(fds[0]); 247 close(fds[1]); 248 } 249 } 250 #endif 251 return 0; 252 } 253 254 if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) return -1; 255 256 #ifdef notyet 257 if ( async ) return -2; 258 #endif 259 260 #ifdef HAVE_POLL 261 { 262 struct pollfd fd; 263 int timeout = INFTIM; 264 265 if( opt_tv != NULL ) timeout = TV2MILLISEC( &tv ); 266 267 fd.fd = s; 268 fd.events = POLL_WRITE; 269 270 do { 271 fd.revents = 0; 272 rc = poll( &fd, 1, timeout ); 273 } while( rc == AC_SOCKET_ERROR && errno == EINTR && 274 LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART )); 275 276 if( rc == AC_SOCKET_ERROR ) return rc; 277 278 if( fd.revents & POLL_WRITE ) { 279 if ( ldap_pvt_is_socket_ready(ld, s) == -1 ) return -1; 280 if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1; 281 #ifdef LDAP_PF_LOCAL_SENDMSG 282 goto sendcred; 283 #else 284 return ( 0 ); 285 #endif 286 } 287 } 288 #else 289 { 290 fd_set wfds, *z=NULL; 291 292 #ifdef FD_SETSIZE 293 if ( s >= FD_SETSIZE ) { 294 rc = AC_SOCKET_ERROR; 295 tcp_close( s ); 296 ldap_pvt_set_errno( EMFILE ); 297 return rc; 298 } 299 #endif 300 do { 301 FD_ZERO(&wfds); 302 FD_SET(s, &wfds ); 303 rc = select( ldap_int_tblsize, z, &wfds, z, opt_tv ? &tv : NULL ); 304 } while( rc == AC_SOCKET_ERROR && errno == EINTR && 305 LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART )); 306 307 if( rc == AC_SOCKET_ERROR ) return rc; 308 309 if ( FD_ISSET(s, &wfds) ) { 310 if ( ldap_pvt_is_socket_ready(ld, s) == -1 ) return -1; 311 if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1; 312 #ifdef LDAP_PF_LOCAL_SENDMSG 313 goto sendcred; 314 #else 315 return ( 0 ); 316 #endif 317 } 318 } 319 #endif 320 321 oslocal_debug(ld, "ldap_connect_timeout: timed out\n",0,0,0); 322 ldap_pvt_set_errno( ETIMEDOUT ); 323 return ( -1 ); 324 } 325 326 int 327 ldap_connect_to_path(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, int async) 328 { 329 struct sockaddr_un server; 330 ber_socket_t s; 331 int rc; 332 const char *path = srv->lud_host; 333 334 oslocal_debug(ld, "ldap_connect_to_path\n",0,0,0); 335 336 if ( path == NULL || path[0] == '\0' ) { 337 path = LDAPI_SOCK; 338 } else { 339 if ( strlen(path) > (sizeof( server.sun_path ) - 1) ) { 340 ldap_pvt_set_errno( ENAMETOOLONG ); 341 return -1; 342 } 343 } 344 345 s = ldap_pvt_socket( ld ); 346 if ( s == AC_SOCKET_INVALID ) { 347 return -1; 348 } 349 350 oslocal_debug(ld, "ldap_connect_to_path: Trying %s\n", path, 0, 0); 351 352 memset( &server, '\0', sizeof(server) ); 353 server.sun_family = AF_LOCAL; 354 strcpy( server.sun_path, path ); 355 356 rc = ldap_pvt_connect(ld, s, &server, async); 357 358 if (rc == 0) { 359 rc = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&server ); 360 } 361 if ( rc ) { 362 ldap_pvt_close_socket(ld, s); 363 } 364 return rc; 365 } 366 #else 367 static int dummy; /* generate also a warning: 'dummy' defined but not used (at least here) */ 368 #endif /* LDAP_PF_LOCAL */ 369