VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/VRDEServerImpl.cpp

Last change on this file was 106164, checked in by vboxsync, 2 months ago

Main/VRDEServerImpl.cpp: Don't overwrite the 'NEGOTIATE' security method. Use case insenitive compare for the method like we do in the server code. Removed unnecessary rollbacks on i_generateServerCertificate failure (since it won't fail after mData.backup()). bugref:10310

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.1 KB
Line 
1/* $Id: VRDEServerImpl.cpp 106164 2024-09-26 10:42:10Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_VRDESERVER
29#include "VRDEServerImpl.h"
30#include "MachineImpl.h"
31#include "VirtualBoxImpl.h"
32#ifdef VBOX_WITH_EXTPACK
33# include "ExtPackManagerImpl.h"
34#endif
35
36#include <iprt/cpp/utils.h>
37#include <iprt/ctype.h>
38#include <iprt/ldr.h>
39#include <iprt/path.h>
40#include <iprt/crypto/x509.h>
41
42#include <VBox/err.h>
43#include <VBox/sup.h>
44#include <VBox/com/array.h>
45
46#include <VBox/RemoteDesktop/VRDE.h>
47
48#include "AutoStateDep.h"
49#include "AutoCaller.h"
50#include "Global.h"
51#include "LoggingNew.h"
52
53// defines
54/////////////////////////////////////////////////////////////////////////////
55#define VRDP_DEFAULT_PORT_STR "3389"
56
57#define VRDE_AUTO_GENENERATED_CERT_FILENAME "VRDEAutoGeneratedCert.pem"
58#define VRDE_AUTO_GENENERATED_PKEY_FILENAME "VRDEAutoGeneratedPrivateKey.pem"
59
60
61// constructor / destructor
62/////////////////////////////////////////////////////////////////////////////
63
64VRDEServer::VRDEServer()
65 : mParent(NULL)
66{
67}
68
69VRDEServer::~VRDEServer()
70{
71}
72
73HRESULT VRDEServer::FinalConstruct()
74{
75 return BaseFinalConstruct();
76}
77
78void VRDEServer::FinalRelease()
79{
80 uninit();
81 BaseFinalRelease();
82}
83
84// public initializer/uninitializer for internal purposes only
85/////////////////////////////////////////////////////////////////////////////
86
87/**
88 * Initializes the VRDP server object.
89 *
90 * @param aParent Handle of the parent object.
91 */
92HRESULT VRDEServer::init(Machine *aParent)
93{
94 LogFlowThisFunc(("aParent=%p\n", aParent));
95
96 ComAssertRet(aParent, E_INVALIDARG);
97
98 /* Enclose the state transition NotReady->InInit->Ready */
99 AutoInitSpan autoInitSpan(this);
100 AssertReturn(autoInitSpan.isOk(), E_FAIL);
101
102 unconst(mParent) = aParent;
103 /* mPeer is left null */
104
105 mData.allocate();
106
107 mData->fEnabled = false;
108
109 /* Confirm a successful initialization */
110 autoInitSpan.setSucceeded();
111
112 return S_OK;
113}
114
115/**
116 * Initializes the object given another object
117 * (a kind of copy constructor). This object shares data with
118 * the object passed as an argument.
119 *
120 * @note This object must be destroyed before the original object
121 * it shares data with is destroyed.
122 *
123 * @note Locks @a aThat object for reading.
124 */
125HRESULT VRDEServer::init(Machine *aParent, VRDEServer *aThat)
126{
127 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
128
129 ComAssertRet(aParent && aThat, E_INVALIDARG);
130
131 /* Enclose the state transition NotReady->InInit->Ready */
132 AutoInitSpan autoInitSpan(this);
133 AssertReturn(autoInitSpan.isOk(), E_FAIL);
134
135 unconst(mParent) = aParent;
136 unconst(mPeer) = aThat;
137
138 AutoCaller thatCaller(aThat);
139 AssertComRCReturnRC(thatCaller.hrc());
140
141 AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
142 mData.share(aThat->mData);
143
144 /* Confirm a successful initialization */
145 autoInitSpan.setSucceeded();
146
147 return S_OK;
148}
149
150/**
151 * Initializes the guest object given another guest object
152 * (a kind of copy constructor). This object makes a private copy of data
153 * of the original object passed as an argument.
154 *
155 * @note Locks @a aThat object for reading.
156 */
157HRESULT VRDEServer::initCopy(Machine *aParent, VRDEServer *aThat)
158{
159 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
160
161 ComAssertRet(aParent && aThat, E_INVALIDARG);
162
163 /* Enclose the state transition NotReady->InInit->Ready */
164 AutoInitSpan autoInitSpan(this);
165 AssertReturn(autoInitSpan.isOk(), E_FAIL);
166
167 unconst(mParent) = aParent;
168 /* mPeer is left null */
169
170 AutoCaller thatCaller(aThat);
171 AssertComRCReturnRC(thatCaller.hrc());
172
173 AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
174 mData.attachCopy(aThat->mData);
175
176 /* Confirm a successful initialization */
177 autoInitSpan.setSucceeded();
178
179 return S_OK;
180}
181
182/**
183 * Uninitializes the instance and sets the ready flag to FALSE.
184 * Called either from FinalRelease() or by the parent when it gets destroyed.
185 */
186void VRDEServer::uninit()
187{
188 LogFlowThisFunc(("\n"));
189
190 /* Enclose the state transition Ready->InUninit->NotReady */
191 AutoUninitSpan autoUninitSpan(this);
192 if (autoUninitSpan.uninitDone())
193 return;
194
195 mData.free();
196
197 unconst(mPeer) = NULL;
198 unconst(mParent) = NULL;
199}
200
201/**
202 * Loads settings from the given machine node.
203 * May be called once right after this object creation.
204 *
205 * @param data Configuration settings.
206 *
207 * @note Locks this object for writing.
208 */
209HRESULT VRDEServer::i_loadSettings(const settings::VRDESettings &data)
210{
211 using namespace settings;
212
213 AutoCaller autoCaller(this);
214 AssertComRCReturnRC(autoCaller.hrc());
215
216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
217 mData.assignCopy(&data);
218
219 return S_OK;
220}
221
222/**
223 * Saves settings to the given machine node.
224 *
225 * @param data Configuration settings.
226 *
227 * @note Locks this object for reading.
228 */
229HRESULT VRDEServer::i_saveSettings(settings::VRDESettings &data)
230{
231 AutoCaller autoCaller(this);
232 AssertComRCReturnRC(autoCaller.hrc());
233
234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
235 data = *mData.data();
236
237 return S_OK;
238}
239
240/**
241 * Auto-generates a self-signed certificate for the VM.
242 *
243 * @note Locks this object for writing.
244 */
245int VRDEServer::i_generateServerCertificate()
246{
247 Utf8Str strServerCertificate(VRDE_AUTO_GENENERATED_CERT_FILENAME);
248 int vrc = mParent->i_calculateFullPath(strServerCertificate, strServerCertificate);
249 AssertRCReturn(vrc, vrc);
250
251 Utf8Str strServerPrivateKey(VRDE_AUTO_GENENERATED_PKEY_FILENAME);
252 vrc = mParent->i_calculateFullPath(strServerPrivateKey, strServerPrivateKey);
253 AssertRCReturn(vrc, vrc);
254
255 AutoReadLock mlock(mParent COMMA_LOCKVAL_SRC_POS);
256 Utf8Str const strVMName = mParent->i_getName();
257 mlock.release();
258
259 vrc = RTCrX509Certificate_GenerateSelfSignedRsa(RTDIGESTTYPE_SHA1, 2048 /*cBits*/, 10 * 365 * RT_SEC_1DAY,
260 0 /*fKeyUsage*/, 0 /*fExtKeyUsage*/, strVMName.c_str() /*pvSubject*/,
261 strServerCertificate.c_str(), strServerPrivateKey.c_str(), NULL /*pErrInfo*/);
262 if (RT_SUCCESS(vrc))
263 {
264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
265 mData.backup();
266
267/** @todo r=bird: These statements may trigger exceptions and leave
268 * dangling server_cert.pem & server_key_private.pem files around.
269 * Since we're not doing an active settings save here (problematic IIRC) there
270 * are probably hundreds more likely ways this could go belly up and leave those
271 * files behind.
272 *
273 * The problem is that the code relies on the _settings_ to decide whether they
274 * are there or not, and if no it creates them. If anything goes wrong before
275 * we can save settings, this function will fail to retify the situation because
276 * the file already exist and RTCrX509Certificate_GenerateSelfSignedRsa won't
277 * overwrite existing files.
278 *
279 * Klaus, some settings saving input required here!
280 */
281 if (!mData->mapProperties["Security/Method"].equalsIgnoreCase("NEGOTIATE"))
282 mData->mapProperties["Security/Method"] = Utf8Str("TLS");
283 mData->mapProperties["Security/ServerCertificate"] = strServerCertificate;
284 mData->mapProperties["Security/ServerPrivateKey"] = strServerPrivateKey;
285
286 /* Done with the properties access. */
287 alock.release();
288 }
289 return vrc;
290}
291
292/**
293 * Checks validity of auto-generated certificates, sets VRDE properties, and
294 * regenerates obsolete or missing files as necessary.
295 *
296 * @note Locks this object for writing.
297 */
298HRESULT VRDEServer::i_certificateRepair(BOOL &certificateGenerated)
299{
300 if ( !mData->mapProperties["Security/Method"].equalsIgnoreCase("RDP")
301 && !mData->mapProperties["Security/Method"].equalsIgnoreCase("None"))
302 {
303 Utf8Str strServerCertificate(VRDE_AUTO_GENENERATED_CERT_FILENAME);
304 int vrc = mParent->i_calculateFullPath(strServerCertificate, strServerCertificate);
305 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
306
307 Utf8Str strServerPrivateKey(VRDE_AUTO_GENENERATED_PKEY_FILENAME);
308 vrc = mParent->i_calculateFullPath(strServerPrivateKey, strServerPrivateKey);
309 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
310
311 bool const fServerPrivateKeyExists = RTFileExists(strServerPrivateKey.c_str());
312 bool const fServerCertificate = RTFileExists(strServerCertificate.c_str());
313 if (fServerPrivateKeyExists && fServerCertificate)
314 {
315 /*
316 * Check that the certificate is valid right now and for the next 365 days.
317 *
318 * The ASSUMPTIONS here are that the automatically generated certificates
319 * are valid for at least two years (currently ~10 years) and that VMs
320 * doesn't typically stay up more than a year before being completely
321 * restarted. The latter assumption is of course a big one, as we've no
322 * control what users do here, but a year seems reasonable while not being
323 * too aggressive.
324 */
325 RTERRINFOSTATIC ErrInfo;
326 RTCRX509CERTIFICATE certificate;
327 vrc = RTCrX509Certificate_ReadFromFile(&certificate, strServerCertificate.c_str(), RTCRX509CERT_READ_F_PEM_ONLY,
328 &g_RTAsn1DefaultAllocator, RTErrInfoInitStatic(&ErrInfo));
329 if (RT_FAILURE(vrc))
330 {
331 RTCrX509Certificate_Delete(&certificate);
332 return setError(VBOX_E_IPRT_ERROR, tr("Failed to read server certificate '%s': %Rrc%#RTeim\n"),
333 strServerCertificate.c_str(), vrc, &ErrInfo.Core);
334 }
335
336 RTTIMESPEC Now;
337 bool const validCert = RTCrX509Validity_IsValidAtTimeSpec(&certificate.TbsCertificate.Validity, RTTimeNow(&Now))
338 && RTCrX509Validity_IsValidAtTimeSpec(&certificate.TbsCertificate.Validity,
339 RTTimeSpecAddSeconds(&Now, 365 * RT_SEC_1DAY_64));
340
341 RTCrX509Certificate_Delete(&certificate);
342
343 Utf8Str const strPath = mData->mapProperties["Security/ServerCertificate"];
344 if (validCert && strPath.isEmpty())
345 {
346 /*
347 * Valid auto-generated certificate and private key files exist but are not assigned to vm property
348 */
349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
350 mData.backup();
351 if (!mData->mapProperties["Security/Method"].equalsIgnoreCase("NEGOTIATE"))
352 mData->mapProperties["Security/Method"] = Utf8Str("TLS");
353 mData->mapProperties["Security/ServerCertificate"] = strServerCertificate;
354 mData->mapProperties["Security/ServerPrivateKey"] = strServerPrivateKey;
355 /* Done with the properties access. */
356 alock.release();
357 certificateGenerated = true;
358 LogRel(("VRDE: Reconfigured using existing '%s' and '%s' files.\n",
359 strServerCertificate.c_str(), strServerPrivateKey.c_str()));
360 }
361 else if ( !validCert
362 && (strPath.isEmpty() || RTStrICmp(RTPathFilename(strPath.c_str()), VRDE_AUTO_GENENERATED_CERT_FILENAME)))
363 {
364 /*
365 * Certificate is not valid so delete the files and create new ones
366 */
367 LogRel(("VRDE: Regenerating expired or expiring certificate files '%s' and '%s'...\n",
368 strServerCertificate.c_str(), strServerPrivateKey.c_str()));
369 RTFileDelete(strServerPrivateKey.c_str());
370 RTFileDelete(strServerCertificate.c_str());
371 vrc = i_generateServerCertificate();
372 if (RT_FAILURE(vrc))
373 return setError(VBOX_E_IPRT_ERROR, tr("Failed to auto generate server key and certificate: (%Rrc)\n"), vrc);
374 certificateGenerated = true;
375 }
376 }
377 /*
378 * If only one of cert/key pair exists, delete the file and generate a new matching.
379 */
380 else if (fServerPrivateKeyExists)
381 {
382 LogRel(("VRDE: Orphaned private key file found. Regenerating certificate files '%s' and '%s'...\n",
383 strServerCertificate.c_str(), strServerPrivateKey.c_str()));
384 RTFileDelete(strServerPrivateKey.c_str());
385 vrc = i_generateServerCertificate();
386 if (RT_FAILURE(vrc))
387 return setError(VBOX_E_IPRT_ERROR, tr("Failed to auto generate server key and certificate: (%Rrc)\n"), vrc);
388 certificateGenerated = true;
389 }
390 else if (fServerCertificate)
391 {
392 LogRel(("VRDE: Orphaned certificate file found. Regenerating certificate files '%s' and '%s'...\n",
393 strServerCertificate.c_str(), strServerPrivateKey.c_str()));
394 RTFileDelete(strServerCertificate.c_str());
395 vrc = i_generateServerCertificate();
396 if (RT_FAILURE(vrc))
397 return setError(VBOX_E_IPRT_ERROR, tr("Failed to auto generate server key and certificate: (%Rrc)\n"), vrc);
398 certificateGenerated = true;
399 }
400 /*
401 * Auto-generated certificate and key files do not exist
402 * If the server certificate property is not set
403 * or indicates an auto-generated certificate should exist, create one
404 */
405 else
406 {
407 Utf8Str const strPath = mData->mapProperties["Security/ServerCertificate"];
408 if (strPath.isEmpty() || RTStrICmp(RTPathFilename(strPath.c_str()), VRDE_AUTO_GENENERATED_CERT_FILENAME) == 0)
409 {
410 LogRel(("VRDE: Generating certificate files '%s' and '%s'...\n",
411 strServerCertificate.c_str(), strServerPrivateKey.c_str()));
412 vrc = i_generateServerCertificate();
413 if (RT_FAILURE(vrc))
414 return setError(VBOX_E_IPRT_ERROR, tr("Failed to auto generate server key and certificate: (%Rrc)\n"), vrc);
415 certificateGenerated = true;
416 }
417 }
418 }
419 return S_OK;
420}
421
422// IVRDEServer properties
423/////////////////////////////////////////////////////////////////////////////
424
425HRESULT VRDEServer::getEnabled(BOOL *aEnabled)
426{
427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
428
429 *aEnabled = mData->fEnabled;
430
431 return S_OK;
432}
433
434HRESULT VRDEServer::setEnabled(BOOL aEnabled)
435{
436 /* the machine can also be in saved state for this property to change */
437 AutoMutableOrSavedOrRunningStateDependency adep(mParent);
438 if (FAILED(adep.hrc())) return adep.hrc();
439
440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
441
442 HRESULT hrc = S_OK;
443
444 if (mData->fEnabled != RT_BOOL(aEnabled))
445 {
446 mData.backup();
447 mData->fEnabled = RT_BOOL(aEnabled);
448
449 /* leave the lock before informing callbacks */
450 alock.release();
451
452 /*
453 * If enabling VRDE and TLS is not explicitly disabled
454 * and there is not an existing certificate
455 * then auto-generate a self-signed certificate for this VM.
456 */
457 if (aEnabled)
458 {
459 BOOL certificateGenerated = false;
460 hrc = i_certificateRepair(certificateGenerated);
461 if (FAILED(hrc))
462 LogRel((("Failed to auto generate server key and certificate: (%Rhrc)\n"), hrc));
463 }
464
465 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
466 mParent->i_setModified(Machine::IsModified_VRDEServer);
467 mlock.release();
468
469 /* Avoid deadlock when i_onVRDEServerChange eventually calls SetExtraData. */
470 adep.release();
471
472 hrc = mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
473 if (FAILED(hrc))
474 {
475 /* Failed to enable/disable the server. Revert the internal state. */
476 adep.add();
477 if (SUCCEEDED(adep.hrc()))
478 {
479 alock.acquire();
480 mData->fEnabled = !RT_BOOL(aEnabled);
481 alock.release();
482 mlock.acquire();
483 mParent->i_setModified(Machine::IsModified_VRDEServer);
484 }
485 }
486 }
487
488 return hrc;
489}
490
491static int i_portParseNumber(uint16_t *pu16Port, const char *pszStart, const char *pszEnd)
492{
493 /* Gets a string of digits, converts to 16 bit port number.
494 * Note: pszStart <= pszEnd is expected, the string contains
495 * only digits and pszEnd points to the char after last
496 * digit.
497 */
498 size_t cch = (size_t)(pszEnd - pszStart);
499 if (cch > 0 && cch <= 5) /* Port is up to 5 decimal digits. */
500 {
501 unsigned uPort = 0;
502 while (pszStart != pszEnd)
503 {
504 uPort = uPort * 10 + (unsigned)(*pszStart - '0');
505 pszStart++;
506 }
507
508 if (uPort != 0 && uPort < 0x10000)
509 {
510 if (pu16Port)
511 *pu16Port = (uint16_t)uPort;
512 return VINF_SUCCESS;
513 }
514 }
515
516 return VERR_INVALID_PARAMETER;
517}
518
519static int i_vrdpServerVerifyPortsString(const com::Utf8Str &aPortRange)
520{
521 const char *pszPortRange = aPortRange.c_str();
522
523 if (!pszPortRange || *pszPortRange == 0) /* Reject empty string. */
524 return VERR_INVALID_PARAMETER;
525
526 /* The string should be like "1000-1010,1020,2000-2003" */
527 while (*pszPortRange)
528 {
529 const char *pszStart = pszPortRange;
530 const char *pszDash = NULL;
531 const char *pszEnd = pszStart;
532
533 while (*pszEnd && *pszEnd != ',')
534 {
535 if (*pszEnd == '-')
536 {
537 if (pszDash != NULL)
538 return VERR_INVALID_PARAMETER; /* More than one '-'. */
539
540 pszDash = pszEnd;
541 }
542 else if (!RT_C_IS_DIGIT(*pszEnd))
543 return VERR_INVALID_PARAMETER;
544
545 pszEnd++;
546 }
547
548 /* Update the next range pointer. */
549 pszPortRange = pszEnd;
550 if (*pszPortRange == ',')
551 {
552 pszPortRange++;
553 }
554
555 /* A probably valid range. Verify and parse it. */
556 int vrc;
557 if (pszDash)
558 {
559 vrc = i_portParseNumber(NULL, pszStart, pszDash);
560 if (RT_SUCCESS(vrc))
561 vrc = i_portParseNumber(NULL, pszDash + 1, pszEnd);
562 }
563 else
564 vrc = i_portParseNumber(NULL, pszStart, pszEnd);
565
566 if (RT_FAILURE(vrc))
567 return vrc;
568 }
569
570 return VINF_SUCCESS;
571}
572
573HRESULT VRDEServer::setVRDEProperty(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
574{
575 LogFlowThisFunc(("\n"));
576
577 /* the machine can also be in saved state for this property to change */
578 AutoMutableOrSavedOrRunningStateDependency adep(mParent);
579 if (FAILED(adep.hrc())) return adep.hrc();
580
581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
582
583 /* Special processing for some "standard" properties. */
584 if (aKey == "TCP/Ports")
585 {
586 /* Verify the string. "0" means the default port. */
587 Utf8Str strPorts = aValue == "0"?
588 VRDP_DEFAULT_PORT_STR:
589 aValue;
590 int vrc = i_vrdpServerVerifyPortsString(strPorts);
591 if (RT_FAILURE(vrc))
592 return E_INVALIDARG;
593
594 if (strPorts != mData->mapProperties["TCP/Ports"])
595 {
596 /* Port value is not verified here because it is up to VRDP transport to
597 * use it. Specifying a wrong port number will cause a running server to
598 * stop. There is no fool proof here.
599 */
600 mData.backup();
601 mData->mapProperties["TCP/Ports"] = strPorts;
602
603 /* leave the lock before informing callbacks */
604 alock.release();
605
606 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
607 mParent->i_setModified(Machine::IsModified_VRDEServer);
608 mlock.release();
609
610 /* Avoid deadlock when i_onVRDEServerChange eventually calls SetExtraData. */
611 adep.release();
612
613 mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
614 }
615 }
616 else
617 {
618 /* Generic properties processing.
619 * Look up the old value first; if nothing's changed then do nothing.
620 */
621 Utf8Str strOldValue;
622
623 settings::StringsMap::const_iterator it = mData->mapProperties.find(aKey);
624 if (it != mData->mapProperties.end())
625 strOldValue = it->second;
626
627 if (strOldValue != aValue)
628 {
629 if (aValue.isEmpty())
630 mData->mapProperties.erase(aKey);
631 else
632 mData->mapProperties[aKey] = aValue;
633
634 /* leave the lock before informing callbacks */
635 alock.release();
636
637 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS);
638 mParent->i_setModified(Machine::IsModified_VRDEServer);
639 mlock.release();
640
641 /* Avoid deadlock when i_onVRDEServerChange eventually calls SetExtraData. */
642 adep.release();
643
644 mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
645 }
646 }
647
648 return S_OK;
649}
650
651HRESULT VRDEServer::getVRDEProperty(const com::Utf8Str &aKey, com::Utf8Str &aValue)
652{
653 /* Special case for the server certificate because it might be created automatically by the code below. */
654 if ( aKey == "Security/ServerCertificate"
655 || aKey == "Security/ServerPrivateKey")
656 {
657 BOOL certificateGenerated = false;
658 HRESULT hrc = i_certificateRepair(certificateGenerated);
659 if (FAILED(hrc))
660 LogRel((("Failed to auto generate server key and certificate: (%Rhrc)\n"), hrc));
661 else if (certificateGenerated)
662 {
663 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS);
664 mParent->i_setModified(Machine::IsModified_VRDEServer);
665 mlock.release();
666 }
667 }
668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
669 settings::StringsMap::const_iterator it = mData->mapProperties.find(aKey);
670 if (it != mData->mapProperties.end())
671 aValue = it->second; // source is a Utf8Str
672 else if (aKey == "TCP/Ports")
673 aValue = VRDP_DEFAULT_PORT_STR;
674
675 return S_OK;
676}
677
678/*
679 * Work around clang being unhappy about PFNVRDESUPPORTEDPROPERTIES
680 * ("exception specifications are not allowed beyond a single level of
681 * indirection"). The original comment for 13.0 check said: "assuming
682 * this issue will be fixed eventually". Well, 13.0 is now out, and
683 * it was not.
684 */
685#define CLANG_EXCEPTION_SPEC_HACK (RT_CLANG_PREREQ(11, 0) /* && !RT_CLANG_PREREQ(13, 0) */)
686
687#if CLANG_EXCEPTION_SPEC_HACK
688static int loadVRDELibrary(const char *pszLibraryName, RTLDRMOD *phmod, void *ppfn)
689#else
690static int loadVRDELibrary(const char *pszLibraryName, RTLDRMOD *phmod, PFNVRDESUPPORTEDPROPERTIES *ppfn)
691#endif
692{
693 int vrc = VINF_SUCCESS;
694
695 RTLDRMOD hmod = NIL_RTLDRMOD;
696
697 RTERRINFOSTATIC ErrInfo;
698 RTErrInfoInitStatic(&ErrInfo);
699 if (RTPathHavePath(pszLibraryName))
700 vrc = SUPR3HardenedLdrLoadPlugIn(pszLibraryName, &hmod, &ErrInfo.Core);
701 else
702 vrc = SUPR3HardenedLdrLoadAppPriv(pszLibraryName, &hmod, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
703 if (RT_SUCCESS(vrc))
704 {
705 vrc = RTLdrGetSymbol(hmod, "VRDESupportedProperties", (void **)ppfn);
706
707 if (RT_FAILURE(vrc) && vrc != VERR_SYMBOL_NOT_FOUND)
708 LogRel(("VRDE: Error resolving symbol '%s', vrc %Rrc.\n", "VRDESupportedProperties", vrc));
709 }
710 else
711 {
712 if (RTErrInfoIsSet(&ErrInfo.Core))
713 LogRel(("VRDE: Error loading the library '%s': %s (%Rrc)\n", pszLibraryName, ErrInfo.Core.pszMsg, vrc));
714 else
715 LogRel(("VRDE: Error loading the library '%s' vrc = %Rrc.\n", pszLibraryName, vrc));
716
717 hmod = NIL_RTLDRMOD;
718 }
719
720 if (RT_SUCCESS(vrc))
721 *phmod = hmod;
722 else
723 {
724 if (hmod != NIL_RTLDRMOD)
725 {
726 RTLdrClose(hmod);
727 hmod = NIL_RTLDRMOD;
728 }
729 }
730
731 return vrc;
732}
733
734HRESULT VRDEServer::getVRDEProperties(std::vector<com::Utf8Str> &aProperties)
735{
736 size_t cProperties = 0;
737 aProperties.resize(0);
738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
739 if (!mData->fEnabled)
740 {
741 return S_OK;
742 }
743 alock.release();
744
745 /*
746 * Check that a VRDE extension pack name is set and resolve it into a
747 * library path.
748 */
749 Bstr bstrExtPack;
750 HRESULT hrc = COMGETTER(VRDEExtPack)(bstrExtPack.asOutParam());
751 Log(("VRDEPROP: get extpack hrc 0x%08X, isEmpty %d\n", hrc, bstrExtPack.isEmpty()));
752 if (FAILED(hrc))
753 return hrc;
754 if (bstrExtPack.isEmpty())
755 return E_FAIL;
756
757 Utf8Str strExtPack(bstrExtPack);
758 Utf8Str strVrdeLibrary;
759 int vrc = VINF_SUCCESS;
760 if (strExtPack.equals(VBOXVRDP_KLUDGE_EXTPACK_NAME))
761 strVrdeLibrary = "VBoxVRDP";
762 else
763 {
764#ifdef VBOX_WITH_EXTPACK
765 VirtualBox *pVirtualBox = mParent->i_getVirtualBox();
766 ExtPackManager *pExtPackMgr = pVirtualBox->i_getExtPackManager();
767 vrc = pExtPackMgr->i_getVrdeLibraryPathForExtPack(&strExtPack, &strVrdeLibrary);
768#else
769 vrc = VERR_FILE_NOT_FOUND;
770#endif
771 }
772 Log(("VRDEPROP: library get vrc %Rrc\n", vrc));
773
774 if (RT_SUCCESS(vrc))
775 {
776 /*
777 * Load the VRDE library and start the server, if it is enabled.
778 */
779 PFNVRDESUPPORTEDPROPERTIES pfn = NULL;
780 RTLDRMOD hmod = NIL_RTLDRMOD;
781#if CLANG_EXCEPTION_SPEC_HACK
782 vrc = loadVRDELibrary(strVrdeLibrary.c_str(), &hmod, (void **)&pfn);
783#else
784 vrc = loadVRDELibrary(strVrdeLibrary.c_str(), &hmod, &pfn);
785#endif
786 Log(("VRDEPROP: load library [%s] vrc %Rrc\n", strVrdeLibrary.c_str(), vrc));
787 if (RT_SUCCESS(vrc))
788 {
789 const char * const *papszNames = pfn();
790
791 if (papszNames)
792 {
793 size_t i;
794 for (i = 0; papszNames[i] != NULL; ++i)
795 {
796 cProperties++;
797 }
798 }
799 Log(("VRDEPROP: %d properties\n", cProperties));
800
801 if (cProperties > 0)
802 {
803 aProperties.resize(cProperties);
804 for (size_t i = 0; i < cProperties && papszNames[i] != NULL; ++i)
805 {
806 aProperties[i] = papszNames[i];
807 }
808 }
809
810 /* Do not forget to unload the library. */
811 RTLdrClose(hmod);
812 hmod = NIL_RTLDRMOD;
813 }
814 }
815
816 if (RT_FAILURE(vrc))
817 {
818 return E_FAIL;
819 }
820
821 return S_OK;
822}
823
824
825HRESULT VRDEServer::getAuthType(AuthType_T *aType)
826{
827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
828
829 *aType = mData->authType;
830
831 return S_OK;
832}
833
834HRESULT VRDEServer::setAuthType(AuthType_T aType)
835{
836 /* the machine can also be in saved state for this property to change */
837 AutoMutableOrSavedOrRunningStateDependency adep(mParent);
838 if (FAILED(adep.hrc())) return adep.hrc();
839
840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
841
842 if (mData->authType != aType)
843 {
844 mData.backup();
845 mData->authType = aType;
846
847 /* leave the lock before informing callbacks */
848 alock.release();
849
850 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
851 mParent->i_setModified(Machine::IsModified_VRDEServer);
852 mlock.release();
853
854 mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
855 }
856
857 return S_OK;
858}
859
860HRESULT VRDEServer::getAuthTimeout(ULONG *aTimeout)
861{
862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
863
864 *aTimeout = mData->ulAuthTimeout;
865
866 return S_OK;
867}
868
869
870HRESULT VRDEServer::setAuthTimeout(ULONG aTimeout)
871{
872 /* the machine can also be in saved state for this property to change */
873 AutoMutableOrSavedOrRunningStateDependency adep(mParent);
874 if (FAILED(adep.hrc())) return adep.hrc();
875
876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
877
878 if (aTimeout != mData->ulAuthTimeout)
879 {
880 mData.backup();
881 mData->ulAuthTimeout = aTimeout;
882
883 /* leave the lock before informing callbacks */
884 alock.release();
885
886 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
887 mParent->i_setModified(Machine::IsModified_VRDEServer);
888 mlock.release();
889
890 /* sunlover 20060131: This setter does not require the notification
891 * really */
892#if 0
893 mParent->onVRDEServerChange();
894#endif
895 }
896
897 return S_OK;
898}
899
900HRESULT VRDEServer::getAuthLibrary(com::Utf8Str &aLibrary)
901{
902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
903 aLibrary = mData->strAuthLibrary;
904 alock.release();
905
906 if (aLibrary.isEmpty())
907 {
908 /* Get the global setting. */
909 ComPtr<ISystemProperties> systemProperties;
910 HRESULT hrc = mParent->i_getVirtualBox()->COMGETTER(SystemProperties)(systemProperties.asOutParam());
911 if (SUCCEEDED(hrc))
912 {
913 Bstr strlib;
914 hrc = systemProperties->COMGETTER(VRDEAuthLibrary)(strlib.asOutParam());
915 if (SUCCEEDED(hrc))
916 aLibrary = Utf8Str(strlib).c_str();
917 }
918
919 if (FAILED(hrc))
920 return setError(hrc, tr("failed to query the library setting\n"));
921 }
922
923 return S_OK;
924}
925
926
927HRESULT VRDEServer::setAuthLibrary(const com::Utf8Str &aLibrary)
928{
929 /* the machine can also be in saved state for this property to change */
930 AutoMutableOrSavedOrRunningStateDependency adep(mParent);
931 if (FAILED(adep.hrc())) return adep.hrc();
932
933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
934
935 if (mData->strAuthLibrary != aLibrary)
936 {
937 mData.backup();
938 mData->strAuthLibrary = aLibrary;
939
940 /* leave the lock before informing callbacks */
941 alock.release();
942
943 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS);
944 mParent->i_setModified(Machine::IsModified_VRDEServer);
945 mlock.release();
946
947 mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
948 }
949
950 return S_OK;
951}
952
953
954HRESULT VRDEServer::getAllowMultiConnection(BOOL *aAllowMultiConnection)
955{
956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 *aAllowMultiConnection = mData->fAllowMultiConnection;
959
960 return S_OK;
961}
962
963
964HRESULT VRDEServer::setAllowMultiConnection(BOOL aAllowMultiConnection)
965{
966 /* the machine can also be in saved state for this property to change */
967 AutoMutableOrSavedOrRunningStateDependency adep(mParent);
968 if (FAILED(adep.hrc())) return adep.hrc();
969
970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
971
972 if (mData->fAllowMultiConnection != RT_BOOL(aAllowMultiConnection))
973 {
974 mData.backup();
975 mData->fAllowMultiConnection = RT_BOOL(aAllowMultiConnection);
976
977 /* leave the lock before informing callbacks */
978 alock.release();
979
980 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
981 mParent->i_setModified(Machine::IsModified_VRDEServer);
982 mlock.release();
983
984 mParent->i_onVRDEServerChange(/* aRestart */ TRUE); /// @todo does it need a restart?
985 }
986
987 return S_OK;
988}
989
990HRESULT VRDEServer::getReuseSingleConnection(BOOL *aReuseSingleConnection)
991{
992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
993
994 *aReuseSingleConnection = mData->fReuseSingleConnection;
995
996 return S_OK;
997}
998
999
1000HRESULT VRDEServer::setReuseSingleConnection(BOOL aReuseSingleConnection)
1001{
1002 AutoMutableOrSavedOrRunningStateDependency adep(mParent);
1003 if (FAILED(adep.hrc())) return adep.hrc();
1004
1005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1006
1007 if (mData->fReuseSingleConnection != RT_BOOL(aReuseSingleConnection))
1008 {
1009 mData.backup();
1010 mData->fReuseSingleConnection = RT_BOOL(aReuseSingleConnection);
1011
1012 /* leave the lock before informing callbacks */
1013 alock.release();
1014
1015 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
1016 mParent->i_setModified(Machine::IsModified_VRDEServer);
1017 mlock.release();
1018
1019 mParent->i_onVRDEServerChange(/* aRestart */ TRUE); /// @todo needs a restart?
1020 }
1021
1022 return S_OK;
1023}
1024
1025HRESULT VRDEServer::getVRDEExtPack(com::Utf8Str &aExtPack)
1026{
1027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1028 Utf8Str strExtPack = mData->strVrdeExtPack;
1029 alock.release();
1030 HRESULT hrc = S_OK;
1031
1032 if (strExtPack.isNotEmpty())
1033 {
1034 if (strExtPack.equals(VBOXVRDP_KLUDGE_EXTPACK_NAME))
1035 hrc = S_OK;
1036 else
1037 {
1038#ifdef VBOX_WITH_EXTPACK
1039 ExtPackManager *pExtPackMgr = mParent->i_getVirtualBox()->i_getExtPackManager();
1040 hrc = pExtPackMgr->i_checkVrdeExtPack(&strExtPack);
1041#else
1042 hrc = setError(E_FAIL, tr("Extension pack '%s' does not exist"), strExtPack.c_str());
1043#endif
1044 }
1045 if (SUCCEEDED(hrc))
1046 aExtPack = strExtPack;
1047 }
1048 else
1049 {
1050 /* Get the global setting. */
1051 ComPtr<ISystemProperties> systemProperties;
1052 hrc = mParent->i_getVirtualBox()->COMGETTER(SystemProperties)(systemProperties.asOutParam());
1053 if (SUCCEEDED(hrc))
1054 {
1055 Bstr bstr;
1056 hrc = systemProperties->COMGETTER(DefaultVRDEExtPack)(bstr.asOutParam());
1057 if (SUCCEEDED(hrc))
1058 aExtPack = bstr;
1059 }
1060 }
1061 return hrc;
1062}
1063
1064// public methods only for internal purposes
1065/////////////////////////////////////////////////////////////////////////////
1066HRESULT VRDEServer::setVRDEExtPack(const com::Utf8Str &aExtPack)
1067{
1068 HRESULT hrc = S_OK;
1069 /* the machine can also be in saved state for this property to change */
1070 AutoMutableOrSavedOrRunningStateDependency adep(mParent);
1071 hrc = adep.hrc();
1072 if (SUCCEEDED(hrc))
1073 {
1074 /*
1075 * If not empty, check the specific extension pack.
1076 */
1077 if (!aExtPack.isEmpty())
1078 {
1079 if (aExtPack.equals(VBOXVRDP_KLUDGE_EXTPACK_NAME))
1080 hrc = S_OK;
1081 else
1082 {
1083#ifdef VBOX_WITH_EXTPACK
1084 ExtPackManager *pExtPackMgr = mParent->i_getVirtualBox()->i_getExtPackManager();
1085 hrc = pExtPackMgr->i_checkVrdeExtPack(&aExtPack);
1086#else
1087 hrc = setError(E_FAIL, tr("Extension pack '%s' does not exist"), aExtPack.c_str());
1088#endif
1089 }
1090 }
1091 if (SUCCEEDED(hrc))
1092 {
1093 /*
1094 * Update the setting if there is an actual change, post an
1095 * change event to trigger a VRDE server restart.
1096 */
1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1098 if (aExtPack != mData->strVrdeExtPack)
1099 {
1100 mData.backup();
1101 mData->strVrdeExtPack = aExtPack;
1102
1103 /* leave the lock before informing callbacks */
1104 alock.release();
1105
1106 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS);
1107 mParent->i_setModified(Machine::IsModified_VRDEServer);
1108 mlock.release();
1109
1110 mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
1111 }
1112 }
1113 }
1114
1115 return hrc;
1116}
1117
1118// public methods only for internal purposes
1119/////////////////////////////////////////////////////////////////////////////
1120
1121/**
1122 * @note Locks this object for writing.
1123 */
1124void VRDEServer::i_rollback()
1125{
1126 /* sanity */
1127 AutoCaller autoCaller(this);
1128 AssertComRCReturnVoid(autoCaller.hrc());
1129
1130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1131
1132 mData.rollback();
1133}
1134
1135/**
1136 * @note Locks this object for writing, together with the peer object (also
1137 * for writing) if there is one.
1138 */
1139void VRDEServer::i_commit()
1140{
1141 /* sanity */
1142 AutoCaller autoCaller(this);
1143 AssertComRCReturnVoid(autoCaller.hrc());
1144
1145 /* sanity too */
1146 AutoCaller peerCaller(mPeer);
1147 AssertComRCReturnVoid(peerCaller.hrc());
1148
1149 /* lock both for writing since we modify both (mPeer is "master" so locked
1150 * first) */
1151 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
1152
1153 if (mData.isBackedUp())
1154 {
1155 mData.commit();
1156 if (mPeer)
1157 {
1158 /* attach new data to the peer and reshare it */
1159 mPeer->mData.attach(mData);
1160 }
1161 }
1162}
1163
1164/**
1165 * @note Locks this object for writing, together with the peer object
1166 * represented by @a aThat (locked for reading).
1167 */
1168void VRDEServer::i_copyFrom(VRDEServer *aThat)
1169{
1170 AssertReturnVoid(aThat != NULL);
1171
1172 /* sanity */
1173 AutoCaller autoCaller(this);
1174 AssertComRCReturnVoid(autoCaller.hrc());
1175
1176 /* sanity too */
1177 AutoCaller thatCaller(aThat);
1178 AssertComRCReturnVoid(thatCaller.hrc());
1179
1180 /* peer is not modified, lock it for reading (aThat is "master" so locked
1181 * first) */
1182 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
1183 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
1184
1185 /* this will back up current data */
1186 mData.assignCopy(aThat->mData);
1187}
1188/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette