1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "system.hxx"
21 
22 #include <o3tl/safeint.hxx>
23 #include <osl/pipe.h>
24 #include <osl/diagnose.h>
25 #include <osl/thread.h>
26 #include <osl/interlck.h>
27 #include <rtl/string.h>
28 #include <rtl/ustring.h>
29 #include <rtl/bootstrap.hxx>
30 #include <sal/log.hxx>
31 
32 #include "sockimpl.hxx"
33 #include "secimpl.hxx"
34 #include "unixerrnostring.hxx"
35 
36 #include <cassert>
37 #include <cstring>
38 
39 #define PIPEDEFAULTPATH     "/tmp"
40 #define PIPEALTERNATEPATH   "/var/tmp"
41 
42 static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options, oslSecurity Security);
43 
44 struct
45 {
46     int            errcode;
47     oslPipeError   error;
48 } const PipeError[]= {
49     { 0,               osl_Pipe_E_None              },  /* no error */
50     { EPROTOTYPE,      osl_Pipe_E_NoProtocol        },  /* Protocol wrong type for socket */
51     { ENOPROTOOPT,     osl_Pipe_E_NoProtocol        },  /* Protocol not available */
52     { EPROTONOSUPPORT, osl_Pipe_E_NoProtocol        },  /* Protocol not supported */
53 #ifdef ESOCKTNOSUPPORT
54     { ESOCKTNOSUPPORT, osl_Pipe_E_NoProtocol        },  /* Socket type not supported */
55 #endif
56     { EPFNOSUPPORT,    osl_Pipe_E_NoProtocol        },  /* Protocol family not supported */
57     { EAFNOSUPPORT,    osl_Pipe_E_NoProtocol        },  /* Address family not supported by */
58                                                         /* protocol family */
59     { ENETRESET,       osl_Pipe_E_NetworkReset      },  /* Network dropped connection because */
60                                                          /* of reset */
61     { ECONNABORTED,    osl_Pipe_E_ConnectionAbort   },  /* Software caused connection abort */
62     { ECONNRESET,      osl_Pipe_E_ConnectionReset   },  /* Connection reset by peer */
63     { ENOBUFS,         osl_Pipe_E_NoBufferSpace     },  /* No buffer space available */
64     { ETIMEDOUT,       osl_Pipe_E_TimedOut          },  /* Connection timed out */
65     { ECONNREFUSED,    osl_Pipe_E_ConnectionRefused },  /* Connection refused */
66     { -1,              osl_Pipe_E_invalidError      }
67 };
68 
osl_PipeErrorFromNative(int nativeType)69 static oslPipeError osl_PipeErrorFromNative(int nativeType)
70 {
71     int i = 0;
72 
73     while ((PipeError[i].error != osl_Pipe_E_invalidError) &&
74            (PipeError[i].errcode != nativeType))
75     {
76         i++;
77     }
78 
79     return PipeError[i].error;
80 }
81 
createPipeImpl()82 static oslPipe createPipeImpl()
83 {
84     oslPipe pPipeImpl;
85 
86     pPipeImpl = static_cast< oslPipe >(calloc(1, sizeof(struct oslPipeImpl)));
87     if (!pPipeImpl)
88         return nullptr;
89 
90     pPipeImpl->m_nRefCount = 1;
91     pPipeImpl->m_bClosed = false;
92 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
93     pPipeImpl->m_bIsInShutdown = false;
94     pPipeImpl->m_bIsAccepting = false;
95 #endif
96 
97     return pPipeImpl;
98 }
99 
destroyPipeImpl(oslPipe pImpl)100 static void destroyPipeImpl(oslPipe pImpl)
101 {
102     if (pImpl)
103         free(pImpl);
104 }
105 
osl_createPipe(rtl_uString * ustrPipeName,oslPipeOptions Options,oslSecurity Security)106 oslPipe SAL_CALL osl_createPipe(rtl_uString *ustrPipeName, oslPipeOptions Options, oslSecurity Security)
107 {
108     oslPipe pPipe = nullptr;
109     rtl_String* strPipeName = nullptr;
110 
111     if (ustrPipeName)
112     {
113         rtl_uString2String(&strPipeName,
114                            rtl_uString_getStr(ustrPipeName),
115                            rtl_uString_getLength(ustrPipeName),
116                            osl_getThreadTextEncoding(),
117                            OUSTRING_TO_OSTRING_CVTFLAGS);
118         char* pszPipeName = rtl_string_getStr(strPipeName);
119         pPipe = osl_psz_createPipe(pszPipeName, Options, Security);
120 
121         if (strPipeName)
122             rtl_string_release(strPipeName);
123     }
124 
125     return pPipe;
126 
127 }
128 
129 static OString
getBootstrapSocketPath()130 getBootstrapSocketPath()
131 {
132     OUString pValue;
133 
134     if (rtl::Bootstrap::get("OSL_SOCKET_PATH", pValue))
135     {
136         return OUStringToOString(pValue, RTL_TEXTENCODING_UTF8);
137     }
138     return "";
139 }
140 
osl_psz_createPipe(const char * pszPipeName,oslPipeOptions Options,oslSecurity Security)141 static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options,
142                                     oslSecurity Security)
143 {
144     int Flags;
145     size_t len;
146     struct sockaddr_un addr;
147 
148     OString name;
149     oslPipe pPipe;
150 
151     if (access(PIPEDEFAULTPATH, W_OK) == 0)
152         name = PIPEDEFAULTPATH;
153     else if (access(PIPEALTERNATEPATH, W_OK) == 0)
154         name = PIPEALTERNATEPATH;
155     else {
156         name = getBootstrapSocketPath ();
157     }
158 
159     name += "/";
160 
161     if (Security)
162     {
163         char Ident[256];
164 
165         Ident[0] = '\0';
166 
167         OSL_VERIFY(osl_psz_getUserIdent(Security, Ident, sizeof(Ident)));
168 
169         name += OString::Concat("OSL_PIPE_") + Ident + "_" + pszPipeName;
170     }
171     else
172     {
173         name += OString::Concat("OSL_PIPE_") + pszPipeName;
174     }
175 
176     if (o3tl::make_unsigned(name.getLength()) >= sizeof addr.sun_path)
177     {
178         SAL_WARN("sal.osl.pipe", "osl_createPipe: pipe name too long");
179         return nullptr;
180     }
181 
182     /* alloc memory */
183     pPipe = createPipeImpl();
184 
185     if (!pPipe)
186         return nullptr;
187 
188     /* create socket */
189     pPipe->m_Socket = socket(AF_UNIX, SOCK_STREAM, 0);
190     if (pPipe->m_Socket < 0)
191     {
192         SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
193         destroyPipeImpl(pPipe);
194         return nullptr;
195     }
196 
197     /* set close-on-exec flag */
198     if ((Flags = fcntl(pPipe->m_Socket, F_GETFD, 0)) != -1)
199     {
200         Flags |= FD_CLOEXEC;
201         if (fcntl(pPipe->m_Socket, F_SETFD, Flags) == -1)
202         {
203             SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno));
204         }
205     }
206 
207     memset(&addr, 0, sizeof(addr));
208 
209     SAL_INFO("sal.osl.pipe", "new pipe on fd " << pPipe->m_Socket << " '" << name << "'");
210 
211     addr.sun_family = AF_UNIX;
212     // coverity[fixed_size_dest : FALSE] - safe, see check above
213     strcpy(addr.sun_path, name.getStr());
214 #if defined(DRAGONFLY)
215     len = SUN_LEN(&addr);
216 #else
217     len = sizeof(addr);
218 #endif
219 
220     if (Options & osl_Pipe_CREATE)
221     {
222         struct stat status;
223 
224         /* check if there exists an orphan filesystem entry */
225         if ((stat(name.getStr(), &status) == 0) &&
226             (S_ISSOCK(status.st_mode) || S_ISFIFO(status.st_mode)))
227         {
228             if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
229             {
230                 close (pPipe->m_Socket);
231                 destroyPipeImpl(pPipe);
232                 return nullptr;
233             }
234 
235             unlink(name.getStr());
236         }
237 
238         /* ok, fs clean */
239         if (bind(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) < 0)
240         {
241             SAL_WARN("sal.osl.pipe", "bind() failed: " << UnixErrnoString(errno));
242             close(pPipe->m_Socket);
243             destroyPipeImpl(pPipe);
244             return nullptr;
245         }
246 
247         /*  Only give access to all if no security handle was specified, otherwise security
248             depends on umask */
249 
250         if (!Security)
251             (void)chmod(name.getStr(),S_IRWXU | S_IRWXG |S_IRWXO);
252 
253         strcpy(pPipe->m_Name, name.getStr()); // safe, see check above
254 
255         if (listen(pPipe->m_Socket, 5) < 0)
256         {
257             SAL_WARN("sal.osl.pipe", "listen() failed: " << UnixErrnoString(errno));
258             // cid#1255391 warns about unlink(name) after stat(name, &status)
259             // above, but the intervening call to bind makes those two clearly
260             // unrelated, as it would fail if name existed at that point in
261             // time:
262             // coverity[toctou] - this is bogus
263             unlink(name.getStr());   /* remove filesystem entry */
264             close(pPipe->m_Socket);
265             destroyPipeImpl(pPipe);
266             return nullptr;
267         }
268 
269         return pPipe;
270     }
271 
272     /* osl_pipe_OPEN */
273     if (access(name.getStr(), F_OK) != -1)
274     {
275         if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
276             return pPipe;
277 
278         SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
279     }
280 
281     close (pPipe->m_Socket);
282     destroyPipeImpl(pPipe);
283     return nullptr;
284 }
285 
osl_acquirePipe(oslPipe pPipe)286 void SAL_CALL osl_acquirePipe(oslPipe pPipe)
287 {
288     osl_atomic_increment(&(pPipe->m_nRefCount));
289 }
290 
osl_releasePipe(oslPipe pPipe)291 void SAL_CALL osl_releasePipe(oslPipe pPipe)
292 {
293     if (!pPipe)
294         return;
295 
296     if (osl_atomic_decrement(&(pPipe->m_nRefCount)) == 0)
297     {
298         if (!pPipe->m_bClosed)
299             osl_closePipe(pPipe);
300 
301         destroyPipeImpl(pPipe);
302     }
303 }
304 
osl_closePipe(oslPipe pPipe)305 void SAL_CALL osl_closePipe(oslPipe pPipe)
306 {
307     int nRet;
308     int ConnFD;
309 
310     if (!pPipe)
311         return;
312 
313     if (pPipe->m_bClosed)
314         return;
315 
316     ConnFD = pPipe->m_Socket;
317 
318     /* Thread does not return from accept on linux, so
319        connect to the accepting pipe
320      */
321 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
322     struct sockaddr_un addr;
323 
324     if (pPipe->m_bIsAccepting)
325     {
326         pPipe->m_bIsInShutdown = true;
327         pPipe->m_Socket = -1;
328 
329         int fd = socket(AF_UNIX, SOCK_STREAM, 0);
330         if (fd < 0)
331         {
332             SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
333             return;
334         }
335 
336         memset(&addr, 0, sizeof(addr));
337 
338         SAL_INFO("sal.osl.pipe", "osl_destroyPipe : Pipe Name '" << pPipe->m_Name << "'");
339 
340         addr.sun_family = AF_UNIX;
341         strcpy(addr.sun_path, pPipe->m_Name); // safe, as both are same size
342 
343         nRet = connect(fd, reinterpret_cast< sockaddr* >(&addr), sizeof(addr));
344         if (nRet < 0)
345             SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
346 
347         close(fd);
348     }
349 #endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
350 
351     nRet = shutdown(ConnFD, 2);
352     if (nRet < 0)
353         SAL_WARN("sal.osl.pipe", "shutdown() failed: " << UnixErrnoString(errno));
354 
355     nRet = close(ConnFD);
356     if (nRet < 0)
357         SAL_WARN("sal.osl.pipe", "close() failed: " << UnixErrnoString(errno));
358 
359     /* remove filesystem entry */
360     if (pPipe->m_Name[0] != '\0')
361         unlink(pPipe->m_Name);
362 
363     pPipe->m_bClosed = true;
364 }
365 
osl_acceptPipe(oslPipe pPipe)366 oslPipe SAL_CALL osl_acceptPipe(oslPipe pPipe)
367 {
368     int s;
369     oslPipe pAcceptedPipe;
370 
371     SAL_WARN_IF(!pPipe, "sal.osl.pipe", "invalid pipe");
372     if (!pPipe)
373         return nullptr;
374 
375     assert(pPipe->m_Name[0] != '\0');  // you cannot have an empty pipe name
376 
377 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
378     pPipe->m_bIsAccepting = true;
379 #endif
380 
381     s = accept(pPipe->m_Socket, nullptr, nullptr);
382 
383 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
384     pPipe->m_bIsAccepting = false;
385 #endif
386 
387     if (s < 0)
388     {
389         SAL_WARN("sal.osl.pipe", "accept() failed: " << UnixErrnoString(errno));
390         return nullptr;
391     }
392 
393 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
394     if (pPipe->m_bIsInShutdown)
395     {
396         close(s);
397         return nullptr;
398     }
399 #endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
400 
401     /* alloc memory */
402     pAcceptedPipe = createPipeImpl();
403 
404     assert(pAcceptedPipe);  // should never be the case that an oslPipe cannot be initialized
405     if (!pAcceptedPipe)
406     {
407         close(s);
408         return nullptr;
409     }
410 
411     /* set close-on-exec flag */
412     int flags;
413     if ((flags = fcntl(s, F_GETFD, 0)) >= 0)
414     {
415         flags |= FD_CLOEXEC;
416         if (fcntl(s, F_SETFD, flags) < 0)
417             SAL_WARN("sal.osl.pipe", "fcntl() failed: " <<  UnixErrnoString(errno));
418     }
419 
420     pAcceptedPipe->m_Socket = s;
421 
422     return pAcceptedPipe;
423 }
424 
osl_receivePipe(oslPipe pPipe,void * pBuffer,sal_Int32 BytesToRead)425 sal_Int32 SAL_CALL osl_receivePipe(oslPipe pPipe,
426                         void* pBuffer,
427                         sal_Int32 BytesToRead)
428 {
429     int nRet = 0;
430 
431     SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_receivePipe: invalid pipe");
432     if (!pPipe)
433     {
434         SAL_WARN("sal.osl.pipe", "osl_receivePipe: Invalid socket");
435         errno=EINVAL;
436         return -1;
437     }
438 
439     nRet = recv(pPipe->m_Socket, pBuffer, BytesToRead, 0);
440 
441     if (nRet < 0)
442         SAL_WARN("sal.osl.pipe", "recv() failed: " << UnixErrnoString(errno));
443 
444     return nRet;
445 }
446 
osl_sendPipe(oslPipe pPipe,const void * pBuffer,sal_Int32 BytesToSend)447 sal_Int32 SAL_CALL osl_sendPipe(oslPipe pPipe,
448                        const void* pBuffer,
449                        sal_Int32 BytesToSend)
450 {
451     int nRet=0;
452 
453     SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_sendPipe: invalid pipe");
454     if (!pPipe)
455     {
456         SAL_WARN("sal.osl.pipe", "osl_sendPipe: Invalid socket");
457         errno=EINVAL;
458         return -1;
459     }
460 
461     nRet = send(pPipe->m_Socket, pBuffer, BytesToSend, 0);
462 
463     if (nRet <= 0)
464         SAL_WARN("sal.osl.pipe", "send() failed: " << UnixErrnoString(errno));
465 
466     return nRet;
467 }
468 
osl_getLastPipeError(SAL_UNUSED_PARAMETER oslPipe)469 oslPipeError SAL_CALL osl_getLastPipeError(SAL_UNUSED_PARAMETER oslPipe)
470 {
471     return osl_PipeErrorFromNative(errno);
472 }
473 
osl_writePipe(oslPipe pPipe,const void * pBuffer,sal_Int32 n)474 sal_Int32 SAL_CALL osl_writePipe(oslPipe pPipe, const void *pBuffer, sal_Int32 n)
475 {
476     /* loop until all desired bytes were send or an error occurred */
477     sal_Int32 BytesSend = 0;
478     sal_Int32 BytesToSend = n;
479 
480     SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_writePipe: invalid pipe"); // osl_sendPipe detects invalid pipe
481     while (BytesToSend > 0)
482     {
483         sal_Int32 RetVal;
484 
485         RetVal= osl_sendPipe(pPipe, pBuffer, BytesToSend);
486 
487         /* error occurred? */
488         if (RetVal <= 0)
489             break;
490 
491         BytesToSend -= RetVal;
492         BytesSend += RetVal;
493         pBuffer= static_cast< char const* >(pBuffer) + RetVal;
494     }
495 
496     return BytesSend;
497 }
498 
osl_readPipe(oslPipe pPipe,void * pBuffer,sal_Int32 n)499 sal_Int32 SAL_CALL osl_readPipe( oslPipe pPipe, void *pBuffer , sal_Int32 n )
500 {
501     /* loop until all desired bytes were read or an error occurred */
502     sal_Int32 BytesRead = 0;
503     sal_Int32 BytesToRead = n;
504 
505     SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_readPipe: invalid pipe"); // osl_receivePipe detects invalid pipe
506     while (BytesToRead > 0)
507     {
508         sal_Int32 RetVal;
509         RetVal= osl_receivePipe(pPipe, pBuffer, BytesToRead);
510 
511         /* error occurred? */
512         if (RetVal <= 0)
513             break;
514 
515         BytesToRead -= RetVal;
516         BytesRead += RetVal;
517         pBuffer= static_cast< char* >(pBuffer) + RetVal;
518     }
519 
520     return BytesRead;
521 }
522 
523 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
524