Skip to content

Commit 723ffeb

Browse files
authored
Add registry authentication in runtime (#40123)
1 parent 9147946 commit 723ffeb

File tree

22 files changed

+886
-208
lines changed

22 files changed

+886
-208
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ set(COMMON_LINK_LIBRARIES
285285
Shlwapi.lib
286286
synchronization.lib
287287
Bcrypt.lib
288+
Crypt32.lib
288289
icu.lib)
289290

290291
set(MSI_LINK_LIBRARIES

packages.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<package id="Microsoft.WSL.DeviceHost" version="1.2.10-0" />
2222
<package id="Microsoft.WSL.Kernel" version="6.6.114.1-1" targetFramework="native" />
2323
<package id="Microsoft.WSL.LinuxSdk" version="1.20.0" targetFramework="native" />
24-
<package id="Microsoft.WSL.TestData" version="0.3.0" />
24+
<package id="Microsoft.WSL.TestData" version="0.4.0" />
2525
<package id="Microsoft.WSL.TestDistro" version="2.7.1-1" />
2626
<package id="Microsoft.WSLg" version="1.0.76" />
2727
<package id="Microsoft.Xaml.Behaviors.WinUI.Managed" version="3.0.0" />

src/windows/WslcSDK/wslcsdk.cpp

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,6 @@ try
685685
containerOptions.ContainerNetwork.ContainerNetworkType = internalContainerSettings->networking;
686686

687687
// TODO: No user access
688-
// containerOptions.Entrypoint;
689688
// containerOptions.Labels;
690689
// containerOptions.LabelsCount;
691690
// containerOptions.StopSignal;
@@ -1186,8 +1185,7 @@ try
11861185

11871186
auto progressCallback = ProgressCallback::CreateIf(options);
11881187

1189-
// TODO: Auth
1190-
return errorInfoWrapper.CaptureResult(internalType->session->PullImage(options->uri, nullptr, progressCallback.get()));
1188+
return errorInfoWrapper.CaptureResult(internalType->session->PullImage(options->uri, options->registryAuth, progressCallback.get()));
11911189
}
11921190
CATCH_RETURN();
11931191

@@ -1282,6 +1280,72 @@ try
12821280
}
12831281
CATCH_RETURN();
12841282

1283+
STDAPI WslcTagSessionImage(_In_ WslcSession session, _In_ const WslcTagImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage)
1284+
try
1285+
{
1286+
ErrorInfoWrapper errorInfoWrapper{errorMessage};
1287+
auto internalType = CheckAndGetInternalType(session);
1288+
RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1289+
RETURN_HR_IF_NULL(E_POINTER, options);
1290+
RETURN_HR_IF_NULL(E_INVALIDARG, options->image);
1291+
RETURN_HR_IF_NULL(E_INVALIDARG, options->repo);
1292+
RETURN_HR_IF_NULL(E_INVALIDARG, options->tag);
1293+
1294+
WSLCTagImageOptions runtimeOptions{};
1295+
runtimeOptions.Image = options->image;
1296+
runtimeOptions.Repo = options->repo;
1297+
runtimeOptions.Tag = options->tag;
1298+
1299+
return errorInfoWrapper.CaptureResult(internalType->session->TagImage(&runtimeOptions));
1300+
}
1301+
CATCH_RETURN();
1302+
1303+
STDAPI WslcPushSessionImage(_In_ WslcSession session, _In_ const WslcPushImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage)
1304+
try
1305+
{
1306+
ErrorInfoWrapper errorInfoWrapper{errorMessage};
1307+
auto internalType = CheckAndGetInternalType(session);
1308+
RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1309+
RETURN_HR_IF_NULL(E_POINTER, options);
1310+
RETURN_HR_IF_NULL(E_INVALIDARG, options->image);
1311+
RETURN_HR_IF_NULL(E_INVALIDARG, options->registryAuth);
1312+
1313+
auto progressCallback = ProgressCallback::CreateIf(options);
1314+
1315+
return errorInfoWrapper.CaptureResult(internalType->session->PushImage(options->image, options->registryAuth, progressCallback.get()));
1316+
}
1317+
CATCH_RETURN();
1318+
1319+
STDAPI WslcSessionAuthenticate(
1320+
_In_ WslcSession session,
1321+
_In_z_ PCSTR serverAddress,
1322+
_In_z_ PCSTR username,
1323+
_In_z_ PCSTR password,
1324+
_Outptr_result_z_ PSTR* identityToken,
1325+
_Outptr_opt_result_z_ PWSTR* errorMessage)
1326+
try
1327+
{
1328+
ErrorInfoWrapper errorInfoWrapper{errorMessage};
1329+
auto internalType = CheckAndGetInternalType(session);
1330+
RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1331+
RETURN_HR_IF_NULL(E_POINTER, serverAddress);
1332+
RETURN_HR_IF_NULL(E_POINTER, username);
1333+
RETURN_HR_IF_NULL(E_POINTER, password);
1334+
RETURN_HR_IF_NULL(E_POINTER, identityToken);
1335+
1336+
*identityToken = nullptr;
1337+
1338+
wil::unique_cotaskmem_ansistring token;
1339+
auto hr = errorInfoWrapper.CaptureResult(internalType->session->Authenticate(serverAddress, username, password, &token));
1340+
if (SUCCEEDED(hr))
1341+
{
1342+
*identityToken = token.release();
1343+
}
1344+
1345+
return errorInfoWrapper;
1346+
}
1347+
CATCH_RETURN();
1348+
12851349
STDAPI WslcListSessionImages(_In_ WslcSession session, _Outptr_result_buffer_(*count) WslcImageInfo** images, _Out_ uint32_t* count)
12861350
try
12871351
{

src/windows/WslcSDK/wslcsdk.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ WslcSetSessionSettingsTimeout
2323
WslcSetSessionSettingsVhd
2424

2525
WslcTerminateSession
26+
WslcSessionAuthenticate
2627
WslcPullSessionImage
2728
WslcImportSessionImage
2829
WslcImportSessionImageFromFile
@@ -32,6 +33,8 @@ WslcDeleteSessionImage
3233
WslcListSessionImages
3334
WslcCreateSessionVhdVolume
3435
WslcDeleteSessionVhdVolume
36+
WslcTagSessionImage
37+
WslcPushSessionImage
3538

3639
WslcSetContainerSettingsDomainName
3740
WslcSetContainerSettingsName

src/windows/WslcSDK/wslcsdk.h

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -392,11 +392,6 @@ typedef struct WslcImageProgressMessage
392392
_Out_ WslcImageProgressDetail detail;
393393
} WslcImageProgressMessage;
394394

395-
typedef struct WslcRegistryAuthenticationInformation
396-
{
397-
// TBD
398-
} WslcRegistryAuthenticationInformation;
399-
400395
// pointer-to-function typedef (unambiguous)
401396
typedef HRESULT(CALLBACK* WslcContainerImageProgressCallback)(const WslcImageProgressMessage* progress, PVOID context);
402397

@@ -406,7 +401,7 @@ typedef struct WslcPullImageOptions
406401
_In_z_ PCSTR uri;
407402
WslcContainerImageProgressCallback progressCallback;
408403
PVOID progressCallbackContext;
409-
_In_opt_ const WslcRegistryAuthenticationInformation* authInfo;
404+
_In_opt_z_ PCSTR registryAuth;
410405
} WslcPullImageOptions;
411406

412407
STDAPI WslcPullSessionImage(_In_ WslcSession session, _In_ const WslcPullImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage);
@@ -457,6 +452,58 @@ typedef struct WslcImageInfo
457452

458453
STDAPI WslcDeleteSessionImage(_In_ WslcSession session, _In_z_ PCSTR nameOrId, _Outptr_opt_result_z_ PWSTR* errorMessage);
459454

455+
typedef struct WslcTagImageOptions
456+
{
457+
_In_z_ PCSTR image; // Source image name or ID.
458+
_In_z_ PCSTR repo; // Target repository name.
459+
_In_z_ PCSTR tag; // Target tag name.
460+
} WslcTagImageOptions;
461+
462+
STDAPI WslcTagSessionImage(_In_ WslcSession session, _In_ const WslcTagImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage);
463+
464+
typedef struct WslcPushImageOptions
465+
{
466+
_In_z_ PCSTR image;
467+
_In_z_ PCSTR registryAuth; // Base64-encoded X-Registry-Auth header value.
468+
_In_opt_ WslcContainerImageProgressCallback progressCallback;
469+
_In_opt_ PVOID progressCallbackContext;
470+
} WslcPushImageOptions;
471+
472+
STDAPI WslcPushSessionImage(_In_ WslcSession session, _In_ const WslcPushImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage);
473+
474+
// Authenticates with a container registry and returns an identity token.
475+
//
476+
// Parameters:
477+
// session
478+
// A valid WslcSession handle.
479+
//
480+
// serverAddress
481+
// The registry server address (e.g. "127.0.0.1:5000").
482+
//
483+
// username
484+
// The username for authentication.
485+
//
486+
// password
487+
// The password for authentication.
488+
//
489+
// identityToken
490+
// On success, receives a pointer to a null-terminated ANSI string
491+
// containing the identity token.
492+
//
493+
// The string is allocated using CoTaskMemAlloc. The caller takes
494+
// ownership of the returned memory and must free it by calling
495+
// CoTaskMemFree when it is no longer needed.
496+
//
497+
// Return Value:
498+
// S_OK on success. Otherwise, an HRESULT error code indicating the failure.
499+
STDAPI WslcSessionAuthenticate(
500+
_In_ WslcSession session,
501+
_In_z_ PCSTR serverAddress,
502+
_In_z_ PCSTR username,
503+
_In_z_ PCSTR password,
504+
_Outptr_result_z_ PSTR* identityToken,
505+
_Outptr_opt_result_z_ PWSTR* errorMessage);
506+
460507
// Retrieves the list of container images
461508
// Parameters:
462509
// session

src/windows/common/WSLCContainerLauncher.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Module Name:
1111
This file contains the implementation for WSLCContainerLauncher.
1212
1313
--*/
14+
15+
#include "precomp.h"
1416
#include "WSLCContainerLauncher.h"
1517

1618
using wsl::windows::common::ClientRunningWSLCProcess;

src/windows/common/WSLCContainerLauncher.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class WSLCContainerLauncher : private WSLCProcessLauncher
7979
void SetDnsSearchDomains(std::vector<std::string>&& DnsSearchDomains);
8080
void SetDnsOptions(std::vector<std::string>&& DnsOptions);
8181

82+
using WSLCProcessLauncher::FormatResult;
8283
using WSLCProcessLauncher::SetUser;
8384
using WSLCProcessLauncher::SetWorkingDirectory;
8485

src/windows/common/wslutil.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Module Name:
1515
#include "precomp.h"
1616
#include "wslutil.h"
1717
#include "WslPluginApi.h"
18+
#include <wincrypt.h>
1819
#include "wslinstallerservice.h"
1920
#include "wslc.h"
2021

@@ -1422,4 +1423,49 @@ catch (...)
14221423
{
14231424
LOG_CAUGHT_EXCEPTION();
14241425
return nullptr;
1426+
}
1427+
1428+
std::string wsl::windows::common::wslutil::Base64Encode(const std::string& input)
1429+
{
1430+
DWORD base64Size = 0;
1431+
THROW_IF_WIN32_BOOL_FALSE(CryptBinaryToStringA(
1432+
reinterpret_cast<const BYTE*>(input.c_str()), static_cast<DWORD>(input.size()), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, nullptr, &base64Size));
1433+
1434+
auto buffer = std::make_unique<char[]>(base64Size);
1435+
THROW_IF_WIN32_BOOL_FALSE(CryptBinaryToStringA(
1436+
reinterpret_cast<const BYTE*>(input.c_str()),
1437+
static_cast<DWORD>(input.size()),
1438+
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
1439+
buffer.get(),
1440+
&base64Size));
1441+
1442+
return std::string(buffer.get());
1443+
}
1444+
1445+
std::string wsl::windows::common::wslutil::Base64Decode(const std::string& encoded)
1446+
{
1447+
DWORD size = 0;
1448+
THROW_IF_WIN32_BOOL_FALSE(CryptStringToBinaryA(
1449+
encoded.c_str(), static_cast<DWORD>(encoded.size()), CRYPT_STRING_BASE64, nullptr, &size, nullptr, nullptr));
1450+
1451+
std::string result(size, '\0');
1452+
THROW_IF_WIN32_BOOL_FALSE(CryptStringToBinaryA(
1453+
encoded.c_str(), static_cast<DWORD>(encoded.size()), CRYPT_STRING_BASE64, reinterpret_cast<BYTE*>(result.data()), &size, nullptr, nullptr));
1454+
1455+
result.resize(size);
1456+
return result;
1457+
}
1458+
1459+
std::string wsl::windows::common::wslutil::BuildRegistryAuthHeader(const std::string& username, const std::string& password, const std::string& serverAddress)
1460+
{
1461+
nlohmann::json authJson = {{"username", username}, {"password", password}, {"serveraddress", serverAddress}};
1462+
1463+
return Base64Encode(authJson.dump());
1464+
}
1465+
1466+
std::string wsl::windows::common::wslutil::BuildRegistryAuthHeader(const std::string& identityToken, const std::string& serverAddress)
1467+
{
1468+
nlohmann::json authJson = {{"identitytoken", identityToken}, {"serveraddress", serverAddress}};
1469+
1470+
return Base64Encode(authJson.dump());
14251471
}

src/windows/common/wslutil.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,4 +330,15 @@ WSLCHandle ToCOMInputHandle(HANDLE Handle);
330330

331331
winrt::Windows::Management::Deployment::PackageVolume GetSystemVolume();
332332

333+
std::string Base64Encode(const std::string& input);
334+
std::string Base64Decode(const std::string& encoded);
335+
336+
// Builds the base64-encoded X-Registry-Auth header value used by Docker APIs
337+
// (PullImage, PushImage, etc.) from the given credentials.
338+
std::string BuildRegistryAuthHeader(const std::string& username, const std::string& password, const std::string& serverAddress);
339+
340+
// Builds the base64-encoded X-Registry-Auth header value from an identity token
341+
// returned by Authenticate().
342+
std::string BuildRegistryAuthHeader(const std::string& identityToken, const std::string& serverAddress);
343+
333344
} // namespace wsl::windows::common::wslutil

src/windows/inc/docker_schema.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,25 @@ struct EmptyRequest
4848
using TResponse = void;
4949
};
5050

51+
struct AuthRequest
52+
{
53+
using TResponse = struct AuthResponse;
54+
55+
std::string username;
56+
std::string password;
57+
std::string serveraddress;
58+
59+
NLOHMANN_DEFINE_TYPE_INTRUSIVE(AuthRequest, username, password, serveraddress);
60+
};
61+
62+
struct AuthResponse
63+
{
64+
std::string Status;
65+
std::optional<std::string> IdentityToken;
66+
67+
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AuthResponse, Status, IdentityToken);
68+
};
69+
5170
struct CreateVolume
5271
{
5372
using TResponse = void;

0 commit comments

Comments
 (0)