cifs: use krb5_kt_default() to determine default keytab location
[jlayton/cifs-utils.git] / asn1.c
1 /* 
2    Unix SMB/CIFS implementation.
3    simple ASN1 routines
4    Copyright (C) Andrew Tridgell 2001
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <string.h>
21 #include <talloc.h>
22 #include <stdint.h>
23
24 #include "replace.h"
25 #include "data_blob.h"
26 #include "asn1.h"
27
28 /* allocate an asn1 structure */
29 struct asn1_data *asn1_init(TALLOC_CTX *mem_ctx)
30 {
31         struct asn1_data *ret = talloc_zero(mem_ctx, struct asn1_data);
32         return ret;
33 }
34
35 /* free an asn1 structure */
36 void asn1_free(struct asn1_data *data)
37 {
38         talloc_free(data);
39 }
40
41 /* write to the ASN1 buffer, advancing the buffer pointer */
42 bool asn1_write(struct asn1_data *data, const void *p, int len)
43 {
44         if (data->has_error)
45                 return false;
46         if (data->length < (size_t)data->ofs + len) {
47                 uint8_t *newp;
48                 newp = talloc_realloc(data, data->data, uint8_t, data->ofs+len);
49                 if (!newp) {
50                         data->has_error = true;
51                         return false;
52                 }
53                 data->data = newp;
54                 data->length = data->ofs+len;
55         }
56         memcpy(data->data + data->ofs, p, len);
57         data->ofs += len;
58         return true;
59 }
60
61 /* useful fn for writing a uint8_t */
62 bool asn1_write_uint8(struct asn1_data *data, uint8_t v)
63 {
64         return asn1_write(data, &v, 1);
65 }
66
67 /* push a tag onto the asn1 data buffer. Used for nested structures */
68 bool asn1_push_tag(struct asn1_data *data, uint8_t tag)
69 {
70         struct nesting *nesting;
71
72         asn1_write_uint8(data, tag);
73         nesting = talloc(data, struct nesting);
74         if (!nesting) {
75                 data->has_error = true;
76                 return false;
77         }
78
79         nesting->start = data->ofs;
80         nesting->next = data->nesting;
81         data->nesting = nesting;
82         return asn1_write_uint8(data, 0xff);
83 }
84
85 /* pop a tag */
86 bool asn1_pop_tag(struct asn1_data *data)
87 {
88         struct nesting *nesting;
89         size_t len;
90
91         nesting = data->nesting;
92
93         if (!nesting) {
94                 data->has_error = true;
95                 return false;
96         }
97         len = data->ofs - (nesting->start+1);
98         /* yes, this is ugly. We don't know in advance how many bytes the length
99            of a tag will take, so we assumed 1 byte. If we were wrong then we 
100            need to correct our mistake */
101         if (len > 0xFFFFFF) {
102                 data->data[nesting->start] = 0x84;
103                 if (!asn1_write_uint8(data, 0)) return false;
104                 if (!asn1_write_uint8(data, 0)) return false;
105                 if (!asn1_write_uint8(data, 0)) return false;
106                 if (!asn1_write_uint8(data, 0)) return false;
107                 memmove(data->data+nesting->start+5, data->data+nesting->start+1, len);
108                 data->data[nesting->start+1] = (len>>24) & 0xFF;
109                 data->data[nesting->start+2] = (len>>16) & 0xFF;
110                 data->data[nesting->start+3] = (len>>8) & 0xFF;
111                 data->data[nesting->start+4] = len&0xff;
112         } else if (len > 0xFFFF) {
113                 data->data[nesting->start] = 0x83;
114                 if (!asn1_write_uint8(data, 0)) return false;
115                 if (!asn1_write_uint8(data, 0)) return false;
116                 if (!asn1_write_uint8(data, 0)) return false;
117                 memmove(data->data+nesting->start+4, data->data+nesting->start+1, len);
118                 data->data[nesting->start+1] = (len>>16) & 0xFF;
119                 data->data[nesting->start+2] = (len>>8) & 0xFF;
120                 data->data[nesting->start+3] = len&0xff;
121         } else if (len > 255) {
122                 data->data[nesting->start] = 0x82;
123                 if (!asn1_write_uint8(data, 0)) return false;
124                 if (!asn1_write_uint8(data, 0)) return false;
125                 memmove(data->data+nesting->start+3, data->data+nesting->start+1, len);
126                 data->data[nesting->start+1] = len>>8;
127                 data->data[nesting->start+2] = len&0xff;
128         } else if (len > 127) {
129                 data->data[nesting->start] = 0x81;
130                 if (!asn1_write_uint8(data, 0)) return false;
131                 memmove(data->data+nesting->start+2, data->data+nesting->start+1, len);
132                 data->data[nesting->start+1] = len;
133         } else {
134                 data->data[nesting->start] = len;
135         }
136
137         data->nesting = nesting->next;
138         talloc_free(nesting);
139         return true;
140 }
141
142 bool ber_write_OID_String(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, const char *OID)
143 {
144         unsigned int v, v2;
145         const char *p = (const char *)OID;
146         char *newp;
147         int i;
148
149         v = strtoul(p, &newp, 10);
150         if (newp[0] != '.') return false;
151         p = newp + 1;
152
153         v2 = strtoul(p, &newp, 10);
154         if (newp[0] != '.') return false;
155         p = newp + 1;
156
157         /*the ber representation can't use more space then the string one */
158         *blob = data_blob_talloc(mem_ctx, NULL, strlen(OID));
159         if (!blob->data) return false;
160
161         blob->data[0] = 40*v + v2;
162
163         i = 1;
164         while (*p) {
165                 v = strtoul(p, &newp, 10);
166                 if (newp[0] == '.') {
167                         p = newp + 1;
168                 } else if (newp[0] == '\0') {
169                         p = newp;
170                 } else {
171                         data_blob_free(blob);
172                         return false;
173                 }
174                 if (v >= (1<<28)) blob->data[i++] = (0x80 | ((v>>28)&0x7f));
175                 if (v >= (1<<21)) blob->data[i++] = (0x80 | ((v>>21)&0x7f));
176                 if (v >= (1<<14)) blob->data[i++] = (0x80 | ((v>>14)&0x7f));
177                 if (v >= (1<<7)) blob->data[i++] = (0x80 | ((v>>7)&0x7f));
178                 blob->data[i++] = (v&0x7f);
179         }
180
181         blob->length = i;
182
183         return true;
184 }
185
186 /* write an object ID to a ASN1 buffer */
187 bool asn1_write_OID(struct asn1_data *data, const char *OID)
188 {
189         DATA_BLOB blob;
190
191         if (!asn1_push_tag(data, ASN1_OID)) return false;
192
193         if (!ber_write_OID_String(NULL, &blob, OID)) {
194                 data->has_error = true;
195                 return false;
196         }
197
198         if (!asn1_write(data, blob.data, blob.length)) {
199                 data_blob_free(&blob);
200                 data->has_error = true;
201                 return false;
202         }
203         data_blob_free(&blob);
204         return asn1_pop_tag(data);
205 }
206
207 /* write an octet string */
208 bool asn1_write_OctetString(struct asn1_data *data, const void *p, size_t length)
209 {
210         asn1_push_tag(data, ASN1_OCTET_STRING);
211         asn1_write(data, p, length);
212         asn1_pop_tag(data);
213         return !data->has_error;
214 }
215