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