1 /* 2 * Copyright 2016-2017, 2021 Hugh McMaster 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include <errno.h> 20 #include "reg.h" 21 22 static DWORD wchar_get_type(const WCHAR *type_name) 23 { 24 DWORD i; 25 26 if (!type_name) 27 return REG_SZ; 28 29 for (i = 0; i < ARRAY_SIZE(type_rels); i++) 30 { 31 if (!wcsicmp(type_rels[i].name, type_name)) 32 return type_rels[i].type; 33 } 34 35 return ~0u; 36 } 37 38 /* hexchar_to_byte from programs/regedit/hexedit.c */ 39 static inline BYTE hexchar_to_byte(WCHAR ch) 40 { 41 if (ch >= '0' && ch <= '9') 42 return ch - '0'; 43 else if (ch >= 'a' && ch <= 'f') 44 return ch - 'a' + 10; 45 else if (ch >= 'A' && ch <= 'F') 46 return ch - 'A' + 10; 47 else 48 return -1; 49 } 50 51 static BOOL get_regdata(const WCHAR *data, DWORD reg_type, WCHAR separator, 52 BYTE **data_bytes, DWORD *size_bytes) 53 { 54 static const WCHAR empty; 55 56 *size_bytes = 0; 57 58 if (!data) data = ∅ 59 60 switch (reg_type) 61 { 62 case REG_NONE: 63 case REG_SZ: 64 case REG_EXPAND_SZ: 65 { 66 *size_bytes = (lstrlenW(data) + 1) * sizeof(WCHAR); 67 *data_bytes = malloc(*size_bytes); 68 lstrcpyW((WCHAR *)*data_bytes, data); 69 break; 70 } 71 case REG_DWORD: 72 /* case REG_DWORD_LITTLE_ENDIAN: */ 73 case REG_DWORD_BIG_ENDIAN: /* Yes, this is correct! */ 74 { 75 LPWSTR rest; 76 unsigned long val; 77 val = wcstoul(data, &rest, (towlower(data[1]) == 'x') ? 16 : 10); 78 if (*rest || data[0] == '-' || (val == ~0u && errno == ERANGE)) { 79 output_message(STRING_MISSING_NUMBER); 80 return FALSE; 81 } 82 *size_bytes = sizeof(DWORD); 83 *data_bytes = malloc(*size_bytes); 84 *(DWORD *)*data_bytes = val; 85 break; 86 } 87 case REG_BINARY: 88 { 89 BYTE hex0, hex1, *ptr; 90 int i = 0, destByteIndex = 0, datalen = lstrlenW(data); 91 92 if (!datalen) return TRUE; 93 94 *size_bytes = ((datalen + datalen % 2) / 2) * sizeof(BYTE); 95 *data_bytes = malloc(*size_bytes); 96 97 if (datalen % 2) 98 { 99 hex1 = hexchar_to_byte(data[i++]); 100 if (hex1 == 0xFF) 101 goto no_hex_data; 102 *data_bytes[destByteIndex++] = hex1; 103 } 104 105 ptr = *data_bytes; 106 107 for (; i + 1 < datalen; i += 2) 108 { 109 hex0 = hexchar_to_byte(data[i]); 110 hex1 = hexchar_to_byte(data[i + 1]); 111 if (hex0 == 0xFF || hex1 == 0xFF) 112 goto no_hex_data; 113 ptr[destByteIndex++] = (hex0 << 4) | hex1; 114 } 115 break; 116 117 no_hex_data: 118 free(*data_bytes); 119 *data_bytes = NULL; 120 output_message(STRING_MISSING_HEXDATA); 121 return FALSE; 122 } 123 case REG_MULTI_SZ: 124 { 125 int i, destindex, len = lstrlenW(data); 126 WCHAR *buffer = malloc((len + 2) * sizeof(WCHAR)); 127 128 for (i = 0, destindex = 0; i < len; i++, destindex++) 129 { 130 if (!separator && data[i] == '\\' && data[i + 1] == '0') 131 { 132 buffer[destindex] = 0; 133 i++; 134 } 135 else if (data[i] == separator) 136 buffer[destindex] = 0; 137 else 138 buffer[destindex] = data[i]; 139 140 if (destindex && !buffer[destindex - 1] && (!buffer[destindex] || destindex == 1)) 141 { 142 free(buffer); 143 output_message(STRING_INVALID_STRING); 144 return FALSE; 145 } 146 } 147 buffer[destindex] = 0; 148 if (destindex && buffer[destindex - 1]) 149 buffer[++destindex] = 0; 150 *size_bytes = (destindex + 1) * sizeof(WCHAR); 151 *data_bytes = (BYTE *)buffer; 152 break; 153 } 154 default: 155 output_message(STRING_UNHANDLED_TYPE, reg_type, data); 156 } 157 158 return TRUE; 159 } 160 161 static int run_add(HKEY root, WCHAR *path, REGSAM sam, WCHAR *value_name, BOOL value_empty, 162 WCHAR *type, WCHAR separator, WCHAR *data, BOOL force) 163 { 164 HKEY hkey; 165 DWORD dispos, data_type, data_size; 166 BYTE *reg_data = NULL; 167 LONG rc; 168 169 if (RegCreateKeyExW(root, path, 0, NULL, REG_OPTION_NON_VOLATILE, 170 KEY_READ|KEY_WRITE|sam, NULL, &hkey, &dispos)) 171 { 172 output_message(STRING_ACCESS_DENIED); 173 return 1; 174 } 175 176 if (!force && dispos == REG_OPENED_EXISTING_KEY) 177 { 178 if (RegQueryValueExW(hkey, value_name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) 179 { 180 if (!ask_confirm(STRING_OVERWRITE_VALUE, value_name)) 181 { 182 RegCloseKey(hkey); 183 output_message(STRING_CANCELLED); 184 return 0; 185 } 186 } 187 } 188 189 data_type = wchar_get_type(type); 190 191 if (data_type == ~0u) 192 { 193 RegCloseKey(hkey); 194 output_message(STRING_UNSUPPORTED_TYPE, type); 195 return 1; 196 } 197 198 if ((data_type == REG_DWORD || data_type == REG_DWORD_BIG_ENDIAN) && !data) 199 { 200 RegCloseKey(hkey); 201 output_message(STRING_INVALID_CMDLINE); 202 return 1; 203 } 204 205 if (!get_regdata(data, data_type, separator, ®_data, &data_size)) 206 { 207 RegCloseKey(hkey); 208 return 1; 209 } 210 211 rc = RegSetValueExW(hkey, value_name, 0, data_type, reg_data, data_size); 212 213 free(reg_data); 214 RegCloseKey(hkey); 215 216 if (rc) 217 { 218 output_message(STRING_ACCESS_DENIED); 219 return 1; 220 } 221 222 output_message(STRING_SUCCESS); 223 224 return 0; 225 } 226 227 int reg_add(int argc, WCHAR *argvW[]) 228 { 229 HKEY root; 230 WCHAR *path, *value_name = NULL, *type = NULL, *data = NULL, separator = '\0'; 231 BOOL value_empty = FALSE, force = FALSE; 232 REGSAM sam = 0; 233 int i; 234 235 if (!parse_registry_key(argvW[2], &root, &path)) 236 return 1; 237 238 for (i = 3; i < argc; i++) 239 { 240 WCHAR *str; 241 242 if (argvW[i][0] != '/' && argvW[i][0] != '-') 243 goto invalid; 244 245 str = &argvW[i][1]; 246 247 if (!lstrcmpiW(str, L"ve")) 248 { 249 if (value_empty) goto invalid; 250 value_empty = TRUE; 251 continue; 252 } 253 else if (!lstrcmpiW(str, L"reg:32")) 254 { 255 if (sam & KEY_WOW64_32KEY) goto invalid; 256 sam |= KEY_WOW64_32KEY; 257 continue; 258 } 259 else if (!lstrcmpiW(str, L"reg:64")) 260 { 261 if (sam & KEY_WOW64_64KEY) goto invalid; 262 sam |= KEY_WOW64_64KEY; 263 continue; 264 } 265 else if (!str[0] || str[1]) 266 goto invalid; 267 268 switch (towlower(*str)) 269 { 270 case 'v': 271 if (value_name || !(value_name = argvW[++i])) 272 goto invalid; 273 break; 274 case 't': 275 if (type || !(type = argvW[++i])) 276 goto invalid; 277 break; 278 case 'd': 279 if (data || !(data = argvW[++i])) 280 goto invalid; 281 break; 282 case 's': 283 str = argvW[++i]; 284 if (separator || !str || lstrlenW(str) != 1) 285 goto invalid; 286 separator = str[0]; 287 break; 288 case 'f': 289 if (force) goto invalid; 290 force = TRUE; 291 break; 292 default: 293 goto invalid; 294 } 295 } 296 297 if (value_name && value_empty) 298 goto invalid; 299 300 if (sam == (KEY_WOW64_32KEY|KEY_WOW64_64KEY)) 301 goto invalid; 302 303 return run_add(root, path, sam, value_name, value_empty, type, separator, data, force); 304 305 invalid: 306 output_message(STRING_INVALID_SYNTAX); 307 output_message(STRING_FUNC_HELP, _wcsupr(argvW[1])); 308 return 1; 309 } 310