1 /* ticket.c --- Low-level ASN.1 Ticket handling.
2  * Copyright (C) 2002-2013 Simon Josefsson
3  *
4  * This file is part of Shishi.
5  *
6  * Shishi is free software; you can redistribute it and/or modify it
7  * 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  * Shishi is distributed in the hope that it will be useful, but
12  * 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 Shishi; if not, see http://www.gnu.org/licenses or write
18  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
19  * Floor, Boston, MA 02110-1301, USA
20  *
21  */
22 
23 #include "internal.h"
24 
25 #define SHISHI_TICKET_DEFAULT_TKTVNO "5"
26 #define SHISHI_TICKET_DEFAULT_TKTVNO_LEN 0
27 
28 /**
29  * shishi_ticket:
30  * @handle: shishi handle as allocated by shishi_init().
31  *
32  * This function creates a new ASN.1 Ticket, populated with some
33  * default values.
34  *
35  * Return value: Returns the ticket or NULL on failure.
36  **/
37 Shishi_asn1
shishi_ticket(Shishi * handle)38 shishi_ticket (Shishi * handle)
39 {
40   Shishi_asn1 node = NULL;
41   int rc;
42 
43   node = shishi_asn1_ticket (handle);
44   if (!node)
45     return NULL;
46 
47   rc = shishi_asn1_write (handle, node, "tkt-vno",
48 			  SHISHI_TICKET_DEFAULT_TKTVNO,
49 			  SHISHI_TICKET_DEFAULT_TKTVNO_LEN);
50   if (rc != SHISHI_OK)
51     goto error;
52 
53   return node;
54 
55 error:
56   shishi_asn1_done (handle, node);
57   return NULL;
58 }
59 
60 /**
61  * shishi_ticket_realm_get:
62  * @handle: shishi handle as allocated by shishi_init().
63  * @ticket: input variable with ticket info.
64  * @realm: output array with newly allocated name of realm in ticket.
65  * @realmlen: size of output array.
66  *
67  * Extract realm from ticket.
68  *
69  * Return value: Returns SHISHI_OK iff successful.
70  **/
71 int
shishi_ticket_realm_get(Shishi * handle,Shishi_asn1 ticket,char ** realm,size_t * realmlen)72 shishi_ticket_realm_get (Shishi * handle,
73 			 Shishi_asn1 ticket, char **realm, size_t * realmlen)
74 {
75   return shishi_asn1_read (handle, ticket, "realm", realm, realmlen);
76 }
77 
78 /**
79  * shishi_ticket_realm_set:
80  * @handle: shishi handle as allocated by shishi_init().
81  * @ticket: input variable with ticket info.
82  * @realm: input array with name of realm.
83  *
84  * Set the realm field in the Ticket.
85  *
86  * Return value: Returns SHISHI_OK iff successful.
87  **/
88 int
shishi_ticket_realm_set(Shishi * handle,Shishi_asn1 ticket,const char * realm)89 shishi_ticket_realm_set (Shishi * handle, Shishi_asn1 ticket,
90 			 const char *realm)
91 {
92   int res;
93 
94   res = shishi_asn1_write (handle, ticket, "realm", realm, 0);
95   if (res != SHISHI_OK)
96     return res;
97 
98   return SHISHI_OK;
99 }
100 
101 /**
102  * shishi_ticket_server:
103  * @handle: Shishi library handle create by shishi_init().
104  * @ticket: ASN.1 Ticket variable to get server name from.
105  * @server: pointer to newly allocated zero terminated string containing
106  *   principal name.  May be %NULL (to only populate @serverlen).
107  * @serverlen: pointer to length of @server on output, excluding terminating
108  *   zero.  May be %NULL (to only populate @server).
109  *
110  * Represent server principal name in Ticket as zero-terminated
111  * string.  The string is allocate by this function, and it is the
112  * responsibility of the caller to deallocate it.  Note that the
113  * output length @serverlen does not include the terminating zero.
114  *
115  * Return value: Returns SHISHI_OK iff successful.
116  **/
117 int
shishi_ticket_server(Shishi * handle,Shishi_asn1 ticket,char ** server,size_t * serverlen)118 shishi_ticket_server (Shishi * handle, Shishi_asn1 ticket,
119 		      char **server, size_t * serverlen)
120 {
121   return shishi_principal_name (handle, ticket, "sname", server, serverlen);
122 }
123 
124 /**
125  * shishi_ticket_sname_set:
126  * @handle: shishi handle as allocated by shishi_init().
127  * @ticket: Ticket variable to set server name field in.
128  * @name_type: type of principial, see Shishi_name_type, usually
129  *             SHISHI_NT_UNKNOWN.
130  * @sname: input array with principal name.
131  *
132  * Set the server name field in the Ticket.
133  *
134  * Return value: Returns SHISHI_OK iff successful.
135  **/
136 int
shishi_ticket_sname_set(Shishi * handle,Shishi_asn1 ticket,Shishi_name_type name_type,char * sname[])137 shishi_ticket_sname_set (Shishi * handle,
138 			 Shishi_asn1 ticket,
139 			 Shishi_name_type name_type, char *sname[])
140 {
141   int res = SHISHI_OK;
142   char *buf;
143   int i;
144 
145   asprintf (&buf, "%d", name_type);
146   res = shishi_asn1_write (handle, ticket, "sname.name-type", buf, 0);
147   free (buf);
148   if (res != SHISHI_OK)
149     return res;
150 
151   res = shishi_asn1_write (handle, ticket, "sname.name-string", NULL, 0);
152   if (res != SHISHI_OK)
153     return res;
154 
155   i = 1;
156   while (sname[i - 1])
157     {
158       res = shishi_asn1_write (handle, ticket, "sname.name-string", "NEW", 1);
159       if (res != SHISHI_OK)
160 	return res;
161 
162       asprintf (&buf, "sname.name-string.?%d", i);
163       res = shishi_asn1_write (handle, ticket, buf, sname[i - 1], 0);
164       free (buf);
165       if (res != SHISHI_OK)
166 	return res;
167 
168       i++;
169     }
170 
171   return SHISHI_OK;
172 }
173 
174 int
shishi_ticket_set_server(Shishi * handle,Shishi_asn1 ticket,const char * server)175 shishi_ticket_set_server (Shishi * handle,
176 			  Shishi_asn1 ticket, const char *server)
177 {
178   char *tmpserver;
179   char **serverbuf;
180   char *tokptr = NULL;
181   int res;
182   int i;
183 
184   tmpserver = xstrdup (server);
185   serverbuf = xmalloc (sizeof (*serverbuf));
186 
187   for (i = 0;
188        (serverbuf[i] = strtok_r (i == 0 ? tmpserver : NULL, "/", &tokptr));
189        i++)
190     {
191       serverbuf = xrealloc (serverbuf, (i + 2) * sizeof (*serverbuf));
192     }
193   res = shishi_ticket_sname_set (handle, ticket,
194 				 SHISHI_NT_PRINCIPAL, serverbuf);
195   if (res != SHISHI_OK)
196     {
197       fprintf (stderr, _("Could not set sname: %s\n"), shishi_error (handle));
198       return res;
199     }
200   free (serverbuf);
201   free (tmpserver);
202 
203   return SHISHI_OK;
204 }
205 
206 int
shishi_ticket_srealmserver_set(Shishi * handle,Shishi_asn1 ticket,const char * realm,const char * server)207 shishi_ticket_srealmserver_set (Shishi * handle,
208 				Shishi_asn1 ticket,
209 				const char *realm, const char *server)
210 {
211   int res;
212 
213   res = shishi_ticket_realm_set (handle, ticket, realm);
214   if (res != SHISHI_OK)
215     return res;
216 
217   res = shishi_ticket_set_server (handle, ticket, server);
218   if (res != SHISHI_OK)
219     return res;
220 
221   return SHISHI_OK;
222 }
223 
224 /**
225  * shishi_ticket_get_enc_part_etype:
226  * @handle: shishi handle as allocated by shishi_init().
227  * @ticket: Ticket variable to get value from.
228  * @etype: output variable that holds the value.
229  *
230  * Extract Ticket.enc-part.etype.
231  *
232  * Return value: Returns SHISHI_OK iff successful.
233  **/
234 int
shishi_ticket_get_enc_part_etype(Shishi * handle,Shishi_asn1 ticket,int32_t * etype)235 shishi_ticket_get_enc_part_etype (Shishi * handle,
236 				  Shishi_asn1 ticket, int32_t * etype)
237 {
238   int res;
239 
240   res = shishi_asn1_read_int32 (handle, ticket, "enc-part.etype", etype);
241 
242   return res;
243 }
244 
245 int
shishi_ticket_decrypt(Shishi * handle,Shishi_asn1 ticket,Shishi_key * key,Shishi_asn1 * encticketpart)246 shishi_ticket_decrypt (Shishi * handle,
247 		       Shishi_asn1 ticket,
248 		       Shishi_key * key, Shishi_asn1 * encticketpart)
249 {
250   int res;
251   int i;
252   char *buf;
253   size_t buflen;
254   char *cipher;
255   size_t cipherlen;
256   int etype;
257 
258   res = shishi_ticket_get_enc_part_etype (handle, ticket, &etype);
259   if (res != SHISHI_OK)
260     return res;
261 
262   if (etype != shishi_key_type (key))
263     return SHISHI_TICKET_BAD_KEYTYPE;
264 
265   res = shishi_asn1_read (handle, ticket, "enc-part.cipher",
266 			  &cipher, &cipherlen);
267   if (res != SHISHI_OK)
268     return res;
269 
270   res = shishi_decrypt (handle, key, SHISHI_KEYUSAGE_ENCTICKETPART,
271 			cipher, cipherlen, &buf, &buflen);
272   free (cipher);
273   if (res != SHISHI_OK)
274     {
275       shishi_error_printf (handle,
276 			   "Ticket decrypt failed, wrong password?\n");
277       return SHISHI_TICKET_DECRYPT_FAILED;
278     }
279 
280   /* The crypto is so 1980; no length indicator. Trim off pad bytes
281      until we can parse it. */
282   for (i = 0; i < 8; i++)
283     {
284       if (VERBOSEASN1 (handle))
285 	printf ("Trying with %d pad in enckdcrep...\n", i);
286 
287       *encticketpart = shishi_der2asn1_encticketpart (handle, &buf[0],
288 						      buflen - i);
289       if (*encticketpart != NULL)
290 	break;
291     }
292 
293   if (*encticketpart == NULL)
294     {
295       shishi_error_printf (handle, "Could not DER decode EncTicketPart. "
296 			   "Password probably correct (decrypt ok) though\n");
297       return SHISHI_ASN1_ERROR;
298     }
299 
300   return SHISHI_OK;
301 }
302 
303 /**
304  * shishi_ticket_set_enc_part:
305  * @handle: shishi handle as allocated by shishi_init().
306  * @ticket: Ticket to add enc-part field to.
307  * @etype: encryption type used to encrypt enc-part.
308  * @kvno: key version number.
309  * @buf: input array with encrypted enc-part.
310  * @buflen: size of input array with encrypted enc-part.
311  *
312  * Set the encrypted enc-part field in the Ticket.  The encrypted data
313  * is usually created by calling shishi_encrypt() on the DER encoded
314  * enc-part.  To save time, you may want to use
315  * shishi_ticket_add_enc_part() instead, which calculates the
316  * encrypted data and calls this function in one step.
317  *
318  * Return value: Returns SHISHI_OK iff successful.
319  **/
320 int
shishi_ticket_set_enc_part(Shishi * handle,Shishi_asn1 ticket,int32_t etype,uint32_t kvno,const char * buf,size_t buflen)321 shishi_ticket_set_enc_part (Shishi * handle,
322 			    Shishi_asn1 ticket,
323 			    int32_t etype, uint32_t kvno,
324 			    const char *buf, size_t buflen)
325 {
326   int res = SHISHI_OK;
327 
328   res = shishi_asn1_write (handle, ticket, "enc-part.cipher", buf, buflen);
329   if (res != SHISHI_OK)
330     return res;
331 
332   res = shishi_asn1_write_int32 (handle, ticket, "enc-part.etype", etype);
333   if (res != SHISHI_OK)
334     return res;
335 
336   if (kvno == UINT32_MAX)
337     res = shishi_asn1_write (handle, ticket, "enc-part.kvno", NULL, 0);
338   else
339     res = shishi_asn1_write_uint32 (handle, ticket, "enc-part.kvno", kvno);
340   if (res != SHISHI_OK)
341     return res;
342 
343   return SHISHI_OK;
344 }
345 
346 /**
347  * shishi_ticket_add_enc_part:
348  * @handle: shishi handle as allocated by shishi_init().
349  * @ticket: Ticket to add enc-part field to.
350  * @key: key used to encrypt enc-part.
351  * @encticketpart: EncTicketPart to add.
352  *
353  * Encrypts DER encoded EncTicketPart using key and stores it in the
354  * Ticket.
355  *
356  * Return value: Returns SHISHI_OK iff successful.
357  **/
358 int
shishi_ticket_add_enc_part(Shishi * handle,Shishi_asn1 ticket,Shishi_key * key,Shishi_asn1 encticketpart)359 shishi_ticket_add_enc_part (Shishi * handle,
360 			    Shishi_asn1 ticket,
361 			    Shishi_key * key, Shishi_asn1 encticketpart)
362 {
363   int res = SHISHI_OK;
364   char *buf;
365   size_t buflen;
366   char *der;
367   size_t derlen;
368 
369   res = shishi_asn1_to_der (handle, encticketpart, &der, &derlen);
370   if (res != SHISHI_OK)
371     {
372       shishi_error_printf (handle, "Could not DER encode encticketpart: %s\n",
373 			   shishi_strerror (res));
374       return res;
375     }
376 
377   res = shishi_encrypt (handle, key, SHISHI_KEYUSAGE_ENCTICKETPART,
378 			der, derlen, &buf, &buflen);
379 
380   free (der);
381 
382   if (res != SHISHI_OK)
383     {
384       shishi_error_printf (handle,
385 			   "Cannot encrypt encrypted part of ticket\n");
386       return res;
387     }
388 
389   res = shishi_ticket_set_enc_part (handle, ticket, shishi_key_type (key),
390 				    shishi_key_version (key), buf, buflen);
391 
392   free (buf);
393 
394   return res;
395 }
396