xref: /NextBSD/contrib/apr-util/crypto/getuuid.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * This attempts to generate V1 UUIDs according to the Internet Draft
19  * located at http://www.webdav.org/specs/draft-leach-uuids-guids-01.txt
20  */
21 #include "apr.h"
22 #include "apr_uuid.h"
23 #include "apr_md5.h"
24 #include "apr_general.h"
25 #include "apr_portable.h"
26 
27 
28 #if APR_HAVE_UNISTD_H
29 #include <unistd.h>     /* for getpid, gethostname */
30 #endif
31 #if APR_HAVE_STDLIB_H
32 #include <stdlib.h>     /* for rand, srand */
33 #endif
34 
35 
36 #if APR_HAVE_STRING_H
37 #include <string.h>
38 #endif
39 #if APR_HAVE_STRINGS_H
40 #include <strings.h>
41 #endif
42 #if APR_HAVE_NETDB_H
43 #include <netdb.h>
44 #endif
45 #if APR_HAVE_SYS_TIME_H
46 #include <sys/time.h>   /* for gettimeofday */
47 #endif
48 
49 #define NODE_LENGTH 6
50 
51 static int uuid_state_seqnum;
52 static unsigned char uuid_state_node[NODE_LENGTH] = { 0 };
53 
54 
get_random_info(unsigned char node[NODE_LENGTH])55 static void get_random_info(unsigned char node[NODE_LENGTH])
56 {
57 #if APR_HAS_RANDOM
58 
59     (void) apr_generate_random_bytes(node, NODE_LENGTH);
60 
61 #else
62 
63     unsigned char seed[APR_MD5_DIGESTSIZE];
64     apr_md5_ctx_t c;
65 
66     /* ### probably should revise some of this to be a bit more portable */
67 
68     /* Leach & Salz use Linux-specific struct sysinfo;
69      * replace with pid/tid for portability (in the spirit of mod_unique_id) */
70     struct {
71 	/* Add thread id here, if applicable, when we get to pthread or apr */
72         pid_t pid;
73 #ifdef NETWARE
74         apr_uint64_t t;
75 #else
76         struct timeval t;
77 #endif
78         char hostname[257];
79 
80     } r;
81 
82     apr_md5_init(&c);
83 #ifdef NETWARE
84     r.pid = NXThreadGetId();
85     NXGetTime(NX_SINCE_BOOT, NX_USECONDS, &(r.t));
86 #else
87     r.pid = getpid();
88     gettimeofday(&r.t, (struct timezone *)0);
89 #endif
90     gethostname(r.hostname, 256);
91     apr_md5_update(&c, (const unsigned char *)&r, sizeof(r));
92     apr_md5_final(seed, &c);
93 
94     memcpy(node, seed, NODE_LENGTH);    /* use a subset of the seed bytes */
95 #endif
96 }
97 
98 /* This implementation generates a random node ID instead of a
99    system-dependent call to get IEEE node ID. This is also more secure:
100    we aren't passing out our MAC address.
101 */
get_pseudo_node_identifier(unsigned char * node)102 static void get_pseudo_node_identifier(unsigned char *node)
103 {
104     get_random_info(node);
105     node[0] |= 0x01;                    /* this designates a random multicast node ID */
106 }
107 
get_system_time(apr_uint64_t * uuid_time)108 static void get_system_time(apr_uint64_t *uuid_time)
109 {
110     /* ### fix this call to be more portable? */
111     *uuid_time = apr_time_now();
112 
113     /* Offset between UUID formatted times and Unix formatted times.
114        UUID UTC base time is October 15, 1582.
115        Unix base time is January 1, 1970.      */
116     *uuid_time = (*uuid_time * 10) + APR_TIME_C(0x01B21DD213814000);
117 }
118 
119 /* true_random -- generate a crypto-quality random number. */
true_random(void)120 static int true_random(void)
121 {
122     apr_uint64_t time_now;
123 
124 #if APR_HAS_RANDOM
125     unsigned char buf[2];
126 
127     if (apr_generate_random_bytes(buf, 2) == APR_SUCCESS) {
128         return (buf[0] << 8) | buf[1];
129     }
130 #endif
131 
132     /* crap. this isn't crypto quality, but it will be Good Enough */
133 
134     time_now = apr_time_now();
135     srand((unsigned int)(((time_now >> 32) ^ time_now) & 0xffffffff));
136 
137     return rand() & 0x0FFFF;
138 }
139 
init_state(void)140 static void init_state(void)
141 {
142     uuid_state_seqnum = true_random();
143     get_pseudo_node_identifier(uuid_state_node);
144 }
145 
get_current_time(apr_uint64_t * timestamp)146 static void get_current_time(apr_uint64_t *timestamp)
147 {
148     /* ### this needs to be made thread-safe! */
149 
150     apr_uint64_t time_now;
151     static apr_uint64_t time_last = 0;
152     static apr_uint64_t fudge = 0;
153 
154     get_system_time(&time_now);
155 
156     /* if clock reading changed since last UUID generated... */
157     if (time_last != time_now) {
158         /* The clock reading has changed since the last UUID was generated.
159            Reset the fudge factor. if we are generating them too fast, then
160            the fudge may need to be reset to something greater than zero. */
161         if (time_last + fudge > time_now)
162             fudge = time_last + fudge - time_now + 1;
163         else
164             fudge = 0;
165         time_last = time_now;
166     }
167     else {
168         /* We generated two really fast. Bump the fudge factor. */
169         ++fudge;
170     }
171 
172     *timestamp = time_now + fudge;
173 }
174 
apr_uuid_get(apr_uuid_t * uuid)175 APU_DECLARE(void) apr_uuid_get(apr_uuid_t *uuid)
176 {
177     apr_uint64_t timestamp;
178     unsigned char *d = uuid->data;
179 
180 #if APR_HAS_OS_UUID
181     if (apr_os_uuid_get(d) == APR_SUCCESS) {
182         return;
183     }
184 #endif /* !APR_HAS_OS_UUID */
185 
186     if (!uuid_state_node[0])
187         init_state();
188 
189     get_current_time(&timestamp);
190 
191     /* time_low, uint32 */
192     d[3] = (unsigned char)timestamp;
193     d[2] = (unsigned char)(timestamp >> 8);
194     d[1] = (unsigned char)(timestamp >> 16);
195     d[0] = (unsigned char)(timestamp >> 24);
196     /* time_mid, uint16 */
197     d[5] = (unsigned char)(timestamp >> 32);
198     d[4] = (unsigned char)(timestamp >> 40);
199     /* time_hi_and_version, uint16 */
200     d[7] = (unsigned char)(timestamp >> 48);
201     d[6] = (unsigned char)(((timestamp >> 56) & 0x0F) | 0x10);
202     /* clock_seq_hi_and_reserved, uint8 */
203     d[8] = (unsigned char)(((uuid_state_seqnum >> 8) & 0x3F) | 0x80);
204     /* clock_seq_low, uint8 */
205     d[9] = (unsigned char)uuid_state_seqnum;
206     /* node, byte[6] */
207     memcpy(&d[10], uuid_state_node, NODE_LENGTH);
208 }
209