1 /*-------------------------------------------------------------------------
2 *
3 * UUID generation functions using the BSD, E2FS or OSSP UUID library
4 *
5 * Copyright (c) 2007-2020, PostgreSQL Global Development Group
6 *
7 * Portions Copyright (c) 2009 Andrew Gierth
8 *
9 * contrib/uuid-ossp/uuid-ossp.c
10 *
11 *-------------------------------------------------------------------------
12 */
13
14 #include "postgres.h"
15
16 #include "fmgr.h"
17 #include "port/pg_bswap.h"
18 #include "utils/builtins.h"
19 #include "utils/uuid.h"
20
21 /*
22 * It's possible that there's more than one uuid.h header file present.
23 * We expect configure to set the HAVE_ symbol for only the one we want.
24 *
25 * BSD includes a uuid_hash() function that conflicts with the one in
26 * builtins.h; we #define it out of the way.
27 */
28 #define uuid_hash bsd_uuid_hash
29
30 #if defined(HAVE_UUID_H)
31 #include <uuid.h>
32 #elif defined(HAVE_OSSP_UUID_H)
33 #include <ossp/uuid.h>
34 #elif defined(HAVE_UUID_UUID_H)
35 #include <uuid/uuid.h>
36 #else
37 #error "please use configure's --with-uuid switch to select a UUID library"
38 #endif
39
40 #undef uuid_hash
41
42 /*
43 * Some BSD variants offer md5 and sha1 implementations but Linux does not,
44 * so we use a copy of the ones from pgcrypto. Not needed with OSSP, though.
45 */
46 #ifndef HAVE_UUID_OSSP
47 #include "md5.h"
48 #include "sha1.h"
49 #endif
50
51
52 /* Check our UUID length against OSSP's; better both be 16 */
53 #if defined(HAVE_UUID_OSSP) && (UUID_LEN != UUID_LEN_BIN)
54 #error UUID length mismatch
55 #endif
56
57 /* Define some constants like OSSP's, to make the code more readable */
58 #ifndef HAVE_UUID_OSSP
59 #define UUID_MAKE_MC 0
60 #define UUID_MAKE_V1 1
61 #define UUID_MAKE_V2 2
62 #define UUID_MAKE_V3 3
63 #define UUID_MAKE_V4 4
64 #define UUID_MAKE_V5 5
65 #endif
66
67 /*
68 * A DCE 1.1 compatible source representation of UUIDs, derived from
69 * the BSD implementation. BSD already has this; OSSP doesn't need it.
70 */
71 #ifdef HAVE_UUID_E2FS
72 typedef struct
73 {
74 uint32_t time_low;
75 uint16_t time_mid;
76 uint16_t time_hi_and_version;
77 uint8_t clock_seq_hi_and_reserved;
78 uint8_t clock_seq_low;
79 uint8_t node[6];
80 } dce_uuid_t;
81 #else
82 #define dce_uuid_t uuid_t
83 #endif
84
85 /* If not OSSP, we need some endianness-manipulation macros */
86 #ifndef HAVE_UUID_OSSP
87
88 #define UUID_TO_NETWORK(uu) \
89 do { \
90 uu.time_low = pg_hton32(uu.time_low); \
91 uu.time_mid = pg_hton16(uu.time_mid); \
92 uu.time_hi_and_version = pg_hton16(uu.time_hi_and_version); \
93 } while (0)
94
95 #define UUID_TO_LOCAL(uu) \
96 do { \
97 uu.time_low = pg_ntoh32(uu.time_low); \
98 uu.time_mid = pg_ntoh16(uu.time_mid); \
99 uu.time_hi_and_version = pg_ntoh16(uu.time_hi_and_version); \
100 } while (0)
101
102 #define UUID_V3_OR_V5(uu, v) \
103 do { \
104 uu.time_hi_and_version &= 0x0FFF; \
105 uu.time_hi_and_version |= (v << 12); \
106 uu.clock_seq_hi_and_reserved &= 0x3F; \
107 uu.clock_seq_hi_and_reserved |= 0x80; \
108 } while(0)
109
110 #endif /* !HAVE_UUID_OSSP */
111
112 PG_MODULE_MAGIC;
113
114 PG_FUNCTION_INFO_V1(uuid_nil);
115 PG_FUNCTION_INFO_V1(uuid_ns_dns);
116 PG_FUNCTION_INFO_V1(uuid_ns_url);
117 PG_FUNCTION_INFO_V1(uuid_ns_oid);
118 PG_FUNCTION_INFO_V1(uuid_ns_x500);
119
120 PG_FUNCTION_INFO_V1(uuid_generate_v1);
121 PG_FUNCTION_INFO_V1(uuid_generate_v1mc);
122 PG_FUNCTION_INFO_V1(uuid_generate_v3);
123 PG_FUNCTION_INFO_V1(uuid_generate_v4);
124 PG_FUNCTION_INFO_V1(uuid_generate_v5);
125
126 #ifdef HAVE_UUID_OSSP
127
128 static void
pguuid_complain(uuid_rc_t rc)129 pguuid_complain(uuid_rc_t rc)
130 {
131 char *err = uuid_error(rc);
132
133 if (err != NULL)
134 ereport(ERROR,
135 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
136 errmsg("OSSP uuid library failure: %s", err)));
137 else
138 ereport(ERROR,
139 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
140 errmsg("OSSP uuid library failure: error code %d", rc)));
141 }
142
143 /*
144 * We create a uuid_t object just once per session and re-use it for all
145 * operations in this module. OSSP UUID caches the system MAC address and
146 * other state in this object. Reusing the object has a number of benefits:
147 * saving the cycles needed to fetch the system MAC address over and over,
148 * reducing the amount of entropy we draw from /dev/urandom, and providing a
149 * positive guarantee that successive generated V1-style UUIDs don't collide.
150 * (On a machine fast enough to generate multiple UUIDs per microsecond,
151 * or whatever the system's wall-clock resolution is, we'd otherwise risk
152 * collisions whenever random initialization of the uuid_t's clock sequence
153 * value chanced to produce duplicates.)
154 *
155 * However: when we're doing V3 or V5 UUID creation, uuid_make needs two
156 * uuid_t objects, one holding the namespace UUID and one for the result.
157 * It's unspecified whether it's safe to use the same uuid_t for both cases,
158 * so let's cache a second uuid_t for use as the namespace holder object.
159 */
160 static uuid_t *
get_cached_uuid_t(int which)161 get_cached_uuid_t(int which)
162 {
163 static uuid_t *cached_uuid[2] = {NULL, NULL};
164
165 if (cached_uuid[which] == NULL)
166 {
167 uuid_rc_t rc;
168
169 rc = uuid_create(&cached_uuid[which]);
170 if (rc != UUID_RC_OK)
171 {
172 cached_uuid[which] = NULL;
173 pguuid_complain(rc);
174 }
175 }
176 return cached_uuid[which];
177 }
178
179 static char *
uuid_to_string(const uuid_t * uuid)180 uuid_to_string(const uuid_t *uuid)
181 {
182 char *buf = palloc(UUID_LEN_STR + 1);
183 void *ptr = buf;
184 size_t len = UUID_LEN_STR + 1;
185 uuid_rc_t rc;
186
187 rc = uuid_export(uuid, UUID_FMT_STR, &ptr, &len);
188 if (rc != UUID_RC_OK)
189 pguuid_complain(rc);
190
191 return buf;
192 }
193
194
195 static void
string_to_uuid(const char * str,uuid_t * uuid)196 string_to_uuid(const char *str, uuid_t *uuid)
197 {
198 uuid_rc_t rc;
199
200 rc = uuid_import(uuid, UUID_FMT_STR, str, UUID_LEN_STR + 1);
201 if (rc != UUID_RC_OK)
202 pguuid_complain(rc);
203 }
204
205
206 static Datum
special_uuid_value(const char * name)207 special_uuid_value(const char *name)
208 {
209 uuid_t *uuid = get_cached_uuid_t(0);
210 char *str;
211 uuid_rc_t rc;
212
213 rc = uuid_load(uuid, name);
214 if (rc != UUID_RC_OK)
215 pguuid_complain(rc);
216 str = uuid_to_string(uuid);
217
218 return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
219 }
220
221 /* len is unused with OSSP, but we want to have the same number of args */
222 static Datum
uuid_generate_internal(int mode,const uuid_t * ns,const char * name,int len)223 uuid_generate_internal(int mode, const uuid_t *ns, const char *name, int len)
224 {
225 uuid_t *uuid = get_cached_uuid_t(0);
226 char *str;
227 uuid_rc_t rc;
228
229 rc = uuid_make(uuid, mode, ns, name);
230 if (rc != UUID_RC_OK)
231 pguuid_complain(rc);
232 str = uuid_to_string(uuid);
233
234 return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
235 }
236
237
238 static Datum
uuid_generate_v35_internal(int mode,pg_uuid_t * ns,text * name)239 uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name)
240 {
241 uuid_t *ns_uuid = get_cached_uuid_t(1);
242
243 string_to_uuid(DatumGetCString(DirectFunctionCall1(uuid_out,
244 UUIDPGetDatum(ns))),
245 ns_uuid);
246
247 return uuid_generate_internal(mode,
248 ns_uuid,
249 text_to_cstring(name),
250 0);
251 }
252
253 #else /* !HAVE_UUID_OSSP */
254
255 static Datum
uuid_generate_internal(int v,unsigned char * ns,const char * ptr,int len)256 uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
257 {
258 char strbuf[40];
259
260 switch (v)
261 {
262 case 0: /* constant-value uuids */
263 strlcpy(strbuf, ptr, 37);
264 break;
265
266 case 1: /* time/node-based uuids */
267 {
268 #ifdef HAVE_UUID_E2FS
269 uuid_t uu;
270
271 uuid_generate_time(uu);
272 uuid_unparse(uu, strbuf);
273
274 /*
275 * PTR, if set, replaces the trailing characters of the uuid;
276 * this is to support v1mc, where a random multicast MAC is
277 * used instead of the physical one
278 */
279 if (ptr && len <= 36)
280 strcpy(strbuf + (36 - len), ptr);
281 #else /* BSD */
282 uuid_t uu;
283 uint32_t status = uuid_s_ok;
284 char *str = NULL;
285
286 uuid_create(&uu, &status);
287
288 if (status == uuid_s_ok)
289 {
290 uuid_to_string(&uu, &str, &status);
291 if (status == uuid_s_ok)
292 {
293 strlcpy(strbuf, str, 37);
294
295 /*
296 * PTR, if set, replaces the trailing characters of
297 * the uuid; this is to support v1mc, where a random
298 * multicast MAC is used instead of the physical one
299 */
300 if (ptr && len <= 36)
301 strcpy(strbuf + (36 - len), ptr);
302 }
303 if (str)
304 free(str);
305 }
306
307 if (status != uuid_s_ok)
308 ereport(ERROR,
309 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
310 errmsg("uuid library failure: %d",
311 (int) status)));
312 #endif
313 break;
314 }
315
316 case 3: /* namespace-based MD5 uuids */
317 case 5: /* namespace-based SHA1 uuids */
318 {
319 dce_uuid_t uu;
320 #ifdef HAVE_UUID_BSD
321 uint32_t status = uuid_s_ok;
322 char *str = NULL;
323 #endif
324
325 if (v == 3)
326 {
327 MD5_CTX ctx;
328
329 MD5Init(&ctx);
330 MD5Update(&ctx, ns, sizeof(uu));
331 MD5Update(&ctx, (unsigned char *) ptr, len);
332 /* we assume sizeof MD5 result is 16, same as UUID size */
333 MD5Final((unsigned char *) &uu, &ctx);
334 }
335 else
336 {
337 SHA1_CTX ctx;
338 unsigned char sha1result[SHA1_RESULTLEN];
339
340 SHA1Init(&ctx);
341 SHA1Update(&ctx, ns, sizeof(uu));
342 SHA1Update(&ctx, (unsigned char *) ptr, len);
343 SHA1Final(sha1result, &ctx);
344 memcpy(&uu, sha1result, sizeof(uu));
345 }
346
347 /* the calculated hash is using local order */
348 UUID_TO_NETWORK(uu);
349 UUID_V3_OR_V5(uu, v);
350
351 #ifdef HAVE_UUID_E2FS
352 /* uuid_unparse expects local order */
353 UUID_TO_LOCAL(uu);
354 uuid_unparse((unsigned char *) &uu, strbuf);
355 #else /* BSD */
356 uuid_to_string(&uu, &str, &status);
357
358 if (status == uuid_s_ok)
359 strlcpy(strbuf, str, 37);
360
361 if (str)
362 free(str);
363
364 if (status != uuid_s_ok)
365 ereport(ERROR,
366 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
367 errmsg("uuid library failure: %d",
368 (int) status)));
369 #endif
370 break;
371 }
372
373 case 4: /* random uuid */
374 default:
375 {
376 #ifdef HAVE_UUID_E2FS
377 uuid_t uu;
378
379 uuid_generate_random(uu);
380 uuid_unparse(uu, strbuf);
381 #else /* BSD */
382 snprintf(strbuf, sizeof(strbuf),
383 "%08lx-%04x-%04x-%04x-%04x%08lx",
384 (unsigned long) arc4random(),
385 (unsigned) (arc4random() & 0xffff),
386 (unsigned) ((arc4random() & 0xfff) | 0x4000),
387 (unsigned) ((arc4random() & 0x3fff) | 0x8000),
388 (unsigned) (arc4random() & 0xffff),
389 (unsigned long) arc4random());
390 #endif
391 break;
392 }
393 }
394
395 return DirectFunctionCall1(uuid_in, CStringGetDatum(strbuf));
396 }
397
398 #endif /* HAVE_UUID_OSSP */
399
400
401 Datum
uuid_nil(PG_FUNCTION_ARGS)402 uuid_nil(PG_FUNCTION_ARGS)
403 {
404 #ifdef HAVE_UUID_OSSP
405 return special_uuid_value("nil");
406 #else
407 return uuid_generate_internal(0, NULL,
408 "00000000-0000-0000-0000-000000000000", 36);
409 #endif
410 }
411
412
413 Datum
uuid_ns_dns(PG_FUNCTION_ARGS)414 uuid_ns_dns(PG_FUNCTION_ARGS)
415 {
416 #ifdef HAVE_UUID_OSSP
417 return special_uuid_value("ns:DNS");
418 #else
419 return uuid_generate_internal(0, NULL,
420 "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36);
421 #endif
422 }
423
424
425 Datum
uuid_ns_url(PG_FUNCTION_ARGS)426 uuid_ns_url(PG_FUNCTION_ARGS)
427 {
428 #ifdef HAVE_UUID_OSSP
429 return special_uuid_value("ns:URL");
430 #else
431 return uuid_generate_internal(0, NULL,
432 "6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36);
433 #endif
434 }
435
436
437 Datum
uuid_ns_oid(PG_FUNCTION_ARGS)438 uuid_ns_oid(PG_FUNCTION_ARGS)
439 {
440 #ifdef HAVE_UUID_OSSP
441 return special_uuid_value("ns:OID");
442 #else
443 return uuid_generate_internal(0, NULL,
444 "6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36);
445 #endif
446 }
447
448
449 Datum
uuid_ns_x500(PG_FUNCTION_ARGS)450 uuid_ns_x500(PG_FUNCTION_ARGS)
451 {
452 #ifdef HAVE_UUID_OSSP
453 return special_uuid_value("ns:X500");
454 #else
455 return uuid_generate_internal(0, NULL,
456 "6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36);
457 #endif
458 }
459
460
461 Datum
uuid_generate_v1(PG_FUNCTION_ARGS)462 uuid_generate_v1(PG_FUNCTION_ARGS)
463 {
464 return uuid_generate_internal(UUID_MAKE_V1, NULL, NULL, 0);
465 }
466
467
468 Datum
uuid_generate_v1mc(PG_FUNCTION_ARGS)469 uuid_generate_v1mc(PG_FUNCTION_ARGS)
470 {
471 #ifdef HAVE_UUID_OSSP
472 char *buf = NULL;
473 #elif defined(HAVE_UUID_E2FS)
474 char strbuf[40];
475 char *buf;
476 uuid_t uu;
477
478 uuid_generate_random(uu);
479
480 /* set IEEE802 multicast and local-admin bits */
481 ((dce_uuid_t *) &uu)->node[0] |= 0x03;
482
483 uuid_unparse(uu, strbuf);
484 buf = strbuf + 24;
485 #else /* BSD */
486 char buf[16];
487
488 /* set IEEE802 multicast and local-admin bits */
489 snprintf(buf, sizeof(buf), "-%04x%08lx",
490 (unsigned) ((arc4random() & 0xffff) | 0x0300),
491 (unsigned long) arc4random());
492 #endif
493
494 return uuid_generate_internal(UUID_MAKE_V1 | UUID_MAKE_MC, NULL,
495 buf, 13);
496 }
497
498
499 Datum
uuid_generate_v3(PG_FUNCTION_ARGS)500 uuid_generate_v3(PG_FUNCTION_ARGS)
501 {
502 pg_uuid_t *ns = PG_GETARG_UUID_P(0);
503 text *name = PG_GETARG_TEXT_PP(1);
504
505 #ifdef HAVE_UUID_OSSP
506 return uuid_generate_v35_internal(UUID_MAKE_V3, ns, name);
507 #else
508 return uuid_generate_internal(UUID_MAKE_V3, (unsigned char *) ns,
509 VARDATA_ANY(name), VARSIZE_ANY_EXHDR(name));
510 #endif
511 }
512
513
514 Datum
uuid_generate_v4(PG_FUNCTION_ARGS)515 uuid_generate_v4(PG_FUNCTION_ARGS)
516 {
517 return uuid_generate_internal(UUID_MAKE_V4, NULL, NULL, 0);
518 }
519
520
521 Datum
uuid_generate_v5(PG_FUNCTION_ARGS)522 uuid_generate_v5(PG_FUNCTION_ARGS)
523 {
524 pg_uuid_t *ns = PG_GETARG_UUID_P(0);
525 text *name = PG_GETARG_TEXT_PP(1);
526
527 #ifdef HAVE_UUID_OSSP
528 return uuid_generate_v35_internal(UUID_MAKE_V5, ns, name);
529 #else
530 return uuid_generate_internal(UUID_MAKE_V5, (unsigned char *) ns,
531 VARDATA_ANY(name), VARSIZE_ANY_EXHDR(name));
532 #endif
533 }
534