1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
/**************************************************************************/
/* */
/* OCaml */
/* */
/* David Allsopp, MetaStack Solutions Ltd. */
/* */
/* Copyright 2015 MetaStack Solutions Ltd. */
/* */
/* All rights reserved. This file is distributed under the terms of */
/* the GNU Lesser General Public License version 2.1, with the */
/* special exception on linking described in the file LICENSE. */
/* */
/**************************************************************************/
#define CAML_INTERNALS
/*
* Windows Vista functions enabled
*/
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/alloc.h>
#include <caml/fail.h>
#include <caml/signals.h>
#include <caml/osdeps.h>
#include <caml/platform.h>
#include "unixsupport.h"
#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
#endif
static _Atomic DWORD additional_symlink_flags = -1;
// Developer Mode allows the creation of symlinks without elevation - see
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
static BOOL IsDeveloperModeEnabled(void)
{
HKEY hKey;
LSTATUS status;
DWORD developerModeRegistryValue, dwordSize = sizeof(DWORD);
status = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock",
0,
KEY_READ | KEY_WOW64_64KEY,
&hKey
);
if (status != ERROR_SUCCESS) {
return FALSE;
}
status = RegQueryValueExW(
hKey,
L"AllowDevelopmentWithoutDevLicense",
NULL,
NULL,
(LPBYTE)&developerModeRegistryValue,
&dwordSize
);
RegCloseKey(hKey);
if (status != ERROR_SUCCESS) {
return FALSE;
}
return developerModeRegistryValue != 0;
}
CAMLprim value caml_unix_symlink(value to_dir, value osource, value odest)
{
CAMLparam3(to_dir, osource, odest);
DWORD flags, additional_flags;
BOOLEAN result;
LPWSTR source;
LPWSTR dest;
caml_unix_check_path(osource, "symlink");
caml_unix_check_path(odest, "symlink");
additional_flags = atomic_load_relaxed(&additional_symlink_flags);
if (additional_flags == -1) {
additional_flags = IsDeveloperModeEnabled() ?
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE : 0;
atomic_store_relaxed(&additional_symlink_flags, additional_flags);
}
flags =
(Bool_val(to_dir) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) | additional_flags;
/* Copy source and dest outside the OCaml heap */
source = caml_stat_strdup_to_utf16(String_val(osource));
dest = caml_stat_strdup_to_utf16(String_val(odest));
caml_enter_blocking_section();
result = CreateSymbolicLink(dest, source, flags);
caml_leave_blocking_section();
caml_stat_free(source);
caml_stat_free(dest);
if (!result) {
caml_win32_maperr(GetLastError());
caml_uerror("symlink", odest);
}
CAMLreturn(Val_unit);
}
#define luid_eq(l, r) (l.LowPart == r.LowPart && l.HighPart == r.HighPart)
CAMLprim value caml_unix_has_symlink(value unit)
{
CAMLparam1(unit);
HANDLE hProcess = GetCurrentProcess();
BOOL result = FALSE;
if (IsDeveloperModeEnabled()) {
CAMLreturn(Val_true);
}
if (OpenProcessToken(hProcess, TOKEN_READ, &hProcess)) {
LUID seCreateSymbolicLinkPrivilege;
if (LookupPrivilegeValue(NULL,
SE_CREATE_SYMBOLIC_LINK_NAME,
&seCreateSymbolicLinkPrivilege)) {
DWORD length;
if (!GetTokenInformation(hProcess, TokenPrivileges, NULL, 0, &length)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
TOKEN_PRIVILEGES* privileges = (TOKEN_PRIVILEGES*)caml_stat_alloc(length);
if (GetTokenInformation(hProcess,
TokenPrivileges,
privileges,
length,
&length)) {
DWORD count = privileges->PrivilegeCount;
if (count) {
LUID_AND_ATTRIBUTES* privs = privileges->Privileges;
while (count-- && !(result = luid_eq(privs->Luid, seCreateSymbolicLinkPrivilege)))
privs++;
}
}
caml_stat_free(privileges);
}
}
}
CloseHandle(hProcess);
}
CAMLreturn(Val_bool(result));
}
|