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