You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

uuid-ossp.c 12KB

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