PostgreSQL Source Code  git master
uuid-ossp.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * UUID generation functions using the BSD, E2FS or OSSP UUID library
4  *
5  * Copyright (c) 2007-2024, 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 "common/cryptohash.h"
17 #include "common/sha1.h"
18 #include "fmgr.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 
106 
112 
118 
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 
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 
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 
237  UUIDPGetDatum(ns))),
238  ns_uuid);
239 
241  ns_uuid,
243  0);
244 }
245 
246 #else /* !HAVE_UUID_OSSP */
247 
248 static Datum
249 uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
250 {
251  char strbuf[40];
252 
253  switch (v)
254  {
255  case 0: /* constant-value uuids */
256  strlcpy(strbuf, ptr, 37);
257  break;
258 
259  case 1: /* time/node-based uuids */
260  {
261 #ifdef HAVE_UUID_E2FS
262  uuid_t uu;
263 
264  uuid_generate_time(uu);
265  uuid_unparse(uu, strbuf);
266 
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  */
272  if (ptr && len <= 36)
273  strcpy(strbuf + (36 - len), ptr);
274 #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
317  break;
318  }
319 
320  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 
329  if (v == 3)
330  {
332 
333  if (pg_cryptohash_init(ctx) < 0)
334  elog(ERROR, "could not initialize %s context: %s", "MD5",
335  pg_cryptohash_error(ctx));
336  if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
337  pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
338  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 */
341  if (pg_cryptohash_final(ctx, (unsigned char *) &uu,
342  sizeof(uu)) < 0)
343  elog(ERROR, "could not finalize %s context: %s", "MD5",
344  pg_cryptohash_error(ctx));
345  pg_cryptohash_free(ctx);
346  }
347  else
348  {
350  unsigned char sha1result[SHA1_DIGEST_LENGTH];
351 
352  if (pg_cryptohash_init(ctx) < 0)
353  elog(ERROR, "could not initialize %s context: %s", "SHA1",
354  pg_cryptohash_error(ctx));
355  if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
356  pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
357  elog(ERROR, "could not update %s context: %s", "SHA1",
358  pg_cryptohash_error(ctx));
359  if (pg_cryptohash_final(ctx, sha1result, sizeof(sha1result)) < 0)
360  elog(ERROR, "could not finalize %s context: %s", "SHA1",
361  pg_cryptohash_error(ctx));
362  pg_cryptohash_free(ctx);
363 
364  memcpy(&uu, sha1result, sizeof(uu));
365  }
366 
367  /* the calculated hash is using local order */
368  UUID_TO_NETWORK(uu);
369  UUID_V3_OR_V5(uu, v);
370 
371 #ifdef HAVE_UUID_E2FS
372  /* uuid_unparse expects local order */
373  UUID_TO_LOCAL(uu);
374  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)));
388 #endif
389  break;
390  }
391 
392  case 4: /* random uuid */
393  default:
394  {
395 #ifdef HAVE_UUID_E2FS
396  uuid_t uu;
397 
398  uuid_generate_random(uu);
399  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());
409 #endif
410  break;
411  }
412  }
413 
415 }
416 
417 #endif /* HAVE_UUID_OSSP */
418 
419 
420 Datum
422 {
423 #ifdef HAVE_UUID_OSSP
424  return special_uuid_value("nil");
425 #else
426  return uuid_generate_internal(0, NULL,
427  "00000000-0000-0000-0000-000000000000", 36);
428 #endif
429 }
430 
431 
432 Datum
434 {
435 #ifdef HAVE_UUID_OSSP
436  return special_uuid_value("ns:DNS");
437 #else
438  return uuid_generate_internal(0, NULL,
439  "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36);
440 #endif
441 }
442 
443 
444 Datum
446 {
447 #ifdef HAVE_UUID_OSSP
448  return special_uuid_value("ns:URL");
449 #else
450  return uuid_generate_internal(0, NULL,
451  "6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36);
452 #endif
453 }
454 
455 
456 Datum
458 {
459 #ifdef HAVE_UUID_OSSP
460  return special_uuid_value("ns:OID");
461 #else
462  return uuid_generate_internal(0, NULL,
463  "6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36);
464 #endif
465 }
466 
467 
468 Datum
470 {
471 #ifdef HAVE_UUID_OSSP
472  return special_uuid_value("ns:X500");
473 #else
474  return uuid_generate_internal(0, NULL,
475  "6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36);
476 #endif
477 }
478 
479 
480 Datum
482 {
483  return uuid_generate_internal(UUID_MAKE_V1, NULL, NULL, 0);
484 }
485 
486 
487 Datum
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;
496 
497  uuid_generate_random(uu);
498 
499  /* set IEEE802 multicast and local-admin bits */
500  ((dce_uuid_t *) &uu)->node[0] |= 0x03;
501 
502  uuid_unparse(uu, strbuf);
503  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
512 
514  buf, 13);
515 }
516 
517 
518 Datum
520 {
521  pg_uuid_t *ns = PG_GETARG_UUID_P(0);
523 
524 #ifdef HAVE_UUID_OSSP
525  return uuid_generate_v35_internal(UUID_MAKE_V3, ns, name);
526 #else
527  return uuid_generate_internal(UUID_MAKE_V3, (unsigned char *) ns,
529 #endif
530 }
531 
532 
533 Datum
535 {
536  return uuid_generate_internal(UUID_MAKE_V4, NULL, NULL, 0);
537 }
538 
539 
540 Datum
542 {
543  pg_uuid_t *ns = PG_GETARG_UUID_P(0);
545 
546 #ifdef HAVE_UUID_OSSP
547  return uuid_generate_v35_internal(UUID_MAKE_V5, ns, name);
548 #else
549  return uuid_generate_internal(UUID_MAKE_V5, (unsigned char *) ns,
551 #endif
552 }
int pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
Definition: cryptohash.c:136
int pg_cryptohash_init(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:100
void pg_cryptohash_free(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:238
pg_cryptohash_ctx * pg_cryptohash_create(pg_cryptohash_type type)
Definition: cryptohash.c:74
int pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
Definition: cryptohash.c:172
const char * pg_cryptohash_error(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:254
@ PG_SHA1
Definition: cryptohash.h:22
@ PG_MD5
Definition: cryptohash.h:21
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
void err(int eval, const char *fmt,...)
Definition: err.c:43
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:642
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define free(a)
Definition: header.h:65
void * palloc(Size size)
Definition: mcxt.c:1304
static PgChecksumMode mode
Definition: pg_checksums.c:56
const void size_t len
static char * buf
Definition: pg_test_fsync.c:73
#define snprintf
Definition: port.h:238
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
static char * DatumGetCString(Datum X)
Definition: postgres.h:335
uintptr_t Datum
Definition: postgres.h:64
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
#define SHA1_DIGEST_LENGTH
Definition: sha1.h:17
Definition: uuid.h:21
Definition: c.h:674
#define dce_uuid_t
Definition: uuid-ossp.c:75
Datum uuid_ns_oid(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:457
#define UUID_MAKE_V4
Definition: uuid-ossp.c:56
#define UUID_MAKE_V3
Definition: uuid-ossp.c:55
Datum uuid_ns_dns(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:433
Datum uuid_nil(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:421
PG_MODULE_MAGIC
Definition: uuid-ossp.c:105
Datum uuid_ns_url(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:445
Datum uuid_generate_v1mc(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:488
Datum uuid_generate_v4(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:534
PG_FUNCTION_INFO_V1(uuid_nil)
#define UUID_MAKE_V1
Definition: uuid-ossp.c:53
Datum uuid_generate_v5(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:541
#define UUID_V3_OR_V5(uu, v)
Definition: uuid-ossp.c:95
Datum uuid_ns_x500(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:469
#define UUID_MAKE_MC
Definition: uuid-ossp.c:52
#define UUID_MAKE_V5
Definition: uuid-ossp.c:57
Datum uuid_generate_v1(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:481
static Datum uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
Definition: uuid-ossp.c:249
#define UUID_TO_LOCAL(uu)
Definition: uuid-ossp.c:88
#define UUID_TO_NETWORK(uu)
Definition: uuid-ossp.c:81
Datum uuid_generate_v3(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:519
static void string_to_uuid(const char *source, pg_uuid_t *uuid, Node *escontext)
Definition: uuid.c:95
Datum uuid_out(PG_FUNCTION_ARGS)
Definition: uuid.c:53
Datum uuid_in(PG_FUNCTION_ARGS)
Definition: uuid.c:42
static Datum UUIDPGetDatum(const pg_uuid_t *X)
Definition: uuid.h:27
#define PG_GETARG_UUID_P(X)
Definition: uuid.h:40
#define VARDATA_ANY(PTR)
Definition: varatt.h:324
#define VARSIZE_ANY_EXHDR(PTR)
Definition: varatt.h:317
char * text_to_cstring(const text *t)
Definition: varlena.c:217
const char * name