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