implemented user access control in NamedPipeListener
[public/netxms.git] / src / libnetxms / nxproc_unix.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** NetXMS Foundation Library
4 ** Copyright (C) 2003-2017 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published
8 ** by the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: nxproc_unix.cpp
21 **
22 **/
23
24 #include "libnetxms.h"
25 #include <nxproc.h>
26 #include <pwd.h>
27
28 /**
29 * Create listener end for named pipe
30 */
31 NamedPipeListener *NamedPipeListener::create(const TCHAR *name, NamedPipeRequestHandler reqHandler, void *userArg, const TCHAR *user)
32 {
33 mode_t prevMask = 0;
34
35 int s = socket(AF_UNIX, SOCK_STREAM, 0);
36 if (s == INVALID_SOCKET)
37 {
38 nxlog_debug(2, _T("NamedPipeListener(%s): socket() call failed (%s)"), name, _tcserror(errno));
39 return NULL;
40 }
41
42 struct sockaddr_un addrLocal;
43 addrLocal.sun_family = AF_UNIX;
44 #ifdef UNICODE
45 sprintf(addrLocal.sun_path, "/tmp/.%S", name);
46 #else
47 sprintf(addrLocal.sun_path, "/tmp/.%s", name);
48 #endif
49 unlink(addrLocal.sun_path);
50 prevMask = umask(S_IWGRP | S_IWOTH);
51 if (bind(s, (struct sockaddr *)&addrLocal, SUN_LEN(&addrLocal)) == -1)
52 {
53 nxlog_debug(2, _T("NamedPipeListener(%s): bind failed (%s)"), name, _tcserror(errno));
54 umask(prevMask);
55 goto failure;
56 }
57 umask(prevMask);
58
59 if (listen(s, 5) == -1)
60 {
61 nxlog_debug(2, _T("NamedPipeListener(%s): listen() call failed (%s)"), name, _tcserror(errno));
62 goto failure;
63 }
64
65 return new NamedPipeListener(name, s, reqHandler, userArg, user);
66
67 failure:
68 close(s);
69 unlink(addrLocal.sun_path);
70 return NULL;
71 }
72
73 /**
74 * Pipe destructor
75 */
76 NamedPipeListener::~NamedPipeListener()
77 {
78 close(m_handle);
79 stop();
80 char path[MAX_PATH];
81 #ifdef UNICODE
82 sprintf(path, "/tmp/.%S", m_name);
83 #else
84 sprintf(path, "/tmp/.%s", m_name);
85 #endif
86 unlink(path);
87 }
88
89 /**
90 * Named pipe server thread
91 */
92 void NamedPipeListener::serverThread()
93 {
94 nxlog_debug(2, _T("NamedPipeListener(%s): waiting for connection"), m_name);
95 while(!m_stop)
96 {
97 struct sockaddr_un addrRemote;
98 socklen_t size = sizeof(struct sockaddr_un);
99 SOCKET cs = accept(m_handle, (struct sockaddr *)&addrRemote, &size);
100 if (cs > 0)
101 {
102 TCHAR user[64];
103
104 struct ucred peer;
105 unsigned int len = sizeof(peer);
106 if (getsockopt(cs, SOL_SOCKET, SO_PEERCRED, &peer, &len) == 0)
107 {
108 #if HAVE_GETPWUID_R
109 struct passwd pwbuf, *pw;
110 char sbuf[4096];
111 getpwuid_r(peer.uid, &pwbuf, sbuf, 4096, &pw);
112 #else
113 struct passwd *pw = getpwuid(peer.uid);
114 #endif
115 if (pw != NULL)
116 {
117 #ifdef UNICODE
118 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pw->pw_name, -1, user, 64);
119 #else
120 nx_strncpy(user, pw->pw_name, 64);
121 #endif
122 }
123 else
124 {
125 _sntprintf(user, 64, _T("[%d]"), (int)peer.uid);
126 }
127 }
128 else
129 {
130 _tcscpy(user, _T("[unknown]"));
131 }
132
133 if ((m_user[0] == 0) || !_tcscmp(m_user, user))
134 {
135 nxlog_debug(5, _T("NamedPipeListener(%s): accepted connection by user %s"), m_name, user);
136 NamedPipe *cp = new NamedPipe(m_name, cs, user);
137 m_reqHandler(cp, m_userArg);
138 delete cp;
139 }
140 else
141 {
142 nxlog_debug(5, _T("NamedPipeListener(%s): rejected connection by user %s"), m_name, user);
143 }
144 }
145 else
146 {
147 nxlog_debug(2, _T("NamedPipeListener(%s): accept failed (%s)"), m_name, _tcserror(errno));
148 }
149 }
150 }
151
152 /**
153 * Pipe destructor
154 */
155 NamedPipe::~NamedPipe()
156 {
157 close(m_handle);
158 MutexDestroy(m_writeLock);
159 }
160
161 /**
162 * Create client end for named pipe
163 */
164 NamedPipe *NamedPipe::connect(const TCHAR *name, UINT32 timeout)
165 {
166 int s = socket(AF_UNIX, SOCK_STREAM, 0);
167 if (s == INVALID_SOCKET)
168 {
169 nxlog_debug(2, _T("NamedPipe(%s): socket() call failed (%s)"), name, _tcserror(errno));
170 return NULL;
171 }
172
173 struct sockaddr_un remote;
174 remote.sun_family = AF_UNIX;
175 #ifdef UNICODE
176 sprintf(remote.sun_path, "/tmp/.%S", name);
177 #else
178 sprintf(remote.sun_path, "/tmp/.%s", name);
179 #endif
180 if (::connect(s, (struct sockaddr *)&remote, SUN_LEN(&remote)) == -1)
181 {
182 close(s);
183 return NULL;
184 }
185
186 return new NamedPipe(name, s, NULL);
187 }
188
189 /**
190 * Write to pipe
191 */
192 bool NamedPipe::write(const void *data, size_t size)
193 {
194 return SendEx(m_handle, data, size, 0, m_writeLock) == (int)size;
195 }