1 /* principal.c --- Get and set default principal.
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 /**
26  * shishi_principal_default_guess:
27  *
28  * Guesses the principal name for the user, looking at environment
29  * variables SHISHI_USER, USER and LOGNAME, or if that fails, returns
30  * the string "user".
31  *
32  * Return value: Returns guessed default principal for user as a
33  * string that has to be deallocated by the caller with free().
34  **/
35 char *
shishi_principal_default_guess(void)36 shishi_principal_default_guess (void)
37 {
38   const char *envuser;
39 
40   envuser = getenv ("SHISHI_USER");
41   if (!envuser)
42     envuser = getenv ("USER");
43   if (!envuser)
44     envuser = getenv ("LOGNAME");
45   if (!envuser)
46     envuser = "user";
47 
48   return xstrdup (envuser);
49 }
50 
51 
52 /**
53  * shishi_principal_default:
54  * @handle: Shishi library handle created by shishi_init().
55  *
56  * The default principal name is the name in the environment variable
57  * USER, or LOGNAME for some systems, but it can be overridden by
58  * specifying the environment variable SHISHI_USER.
59  *
60  * Return value: Returns the default principal name used by the
61  * library.  (Not a copy of it, so don't modify or deallocate it.)
62  **/
63 const char *
shishi_principal_default(Shishi * handle)64 shishi_principal_default (Shishi * handle)
65 {
66   if (!handle->default_principal)
67     {
68       char *p;
69       p = shishi_principal_default_guess ();
70       shishi_principal_default_set (handle, p);
71       free (p);
72     }
73 
74   return handle->default_principal;
75 }
76 
77 /**
78  * shishi_principal_default_set:
79  * @handle: Shishi library handle created by shishi_init().
80  * @principal: string with new default principal name, or NULL to
81  * reset to default.
82  *
83  * Set the default principal used by the library.  The string is copied
84  * into the library, so you can dispose of the variable immediately
85  * after calling this function.
86  **/
87 void
shishi_principal_default_set(Shishi * handle,const char * principal)88 shishi_principal_default_set (Shishi * handle, const char *principal)
89 {
90   free (handle->default_principal);
91   if (principal)
92     handle->default_principal = xstrdup (principal);
93   else
94     handle->default_principal = NULL;
95 }
96 
97 /**
98  * shishi_parse_name:
99  * @handle: Shishi library handle created by shishi_init().
100  * @name: input principal name string, e.g. imap/mail.gnu.org\@GNU.ORG.
101  * @principal: newly allocated output string with principal name.
102  * @realm: newly allocated output string with realm name.
103  *
104  * Split principal name (e.g., "simon\@JOSEFSSON.ORG") into two
105  * newly allocated strings, the @principal ("simon"), and the @realm
106  * ("JOSEFSSON.ORG").  If there is no realm part in @name, @realm is set
107  * to NULL.
108  *
109  * Return value: Returns SHISHI_INVALID_PRINCIPAL_NAME if @name is NULL
110  *   or ends with the escape character "\", and SHISHI_OK if
111  *   successful.
112  **/
113 int
shishi_parse_name(Shishi * handle,const char * name,char ** principal,char ** realm)114 shishi_parse_name (Shishi * handle, const char *name,
115 		   char **principal, char **realm)
116 {
117   const char *p = name;
118   const char *q;
119   int escaped = 0;
120 
121   if (!name)
122     {
123       shishi_error_printf (handle, "Name is NULL\n");
124       return SHISHI_INVALID_PRINCIPAL_NAME;
125     }
126 
127   while (*p && (*p != '@' || escaped))
128     if (escaped)
129       escaped = 0;
130     else if (*p++ == '\\')
131       escaped = 1;
132 
133   if (escaped)
134     {
135       shishi_error_printf (handle,
136 			   "Principal ended with escape character: %s\n",
137 			   name);
138       return SHISHI_INVALID_PRINCIPAL_NAME;
139     }
140 
141   if (principal)
142     {
143       *principal = xstrndup (name, p - name + 1);
144       (*principal)[p - name] = '\0';
145     }
146 
147   if (*p)
148     {
149       q = ++p;
150 
151       while (*q)
152 	if (escaped)
153 	  escaped = 0;
154 	else if (*q++ == '\\')
155 	  escaped = 1;
156 
157       if (escaped)
158 	{
159 	  shishi_error_printf (handle,
160 			       "Realm ended with escape character: %s\n",
161 			       name);
162 	  return SHISHI_INVALID_PRINCIPAL_NAME;
163 	}
164 
165       if (realm)
166 	*realm = xstrdup (p);
167     }
168   else if (realm)
169     *realm = NULL;
170 
171   return SHISHI_OK;
172 }
173 
174 /**
175  * shishi_principal_name:
176  * @handle: Shishi library handle created by shishi_init().
177  * @namenode: ASN.1 structure with principal in @namefield.
178  * @namefield: name of field in @namenode containing principal name.
179  * @out: pointer to newly allocated, null terminated, string containing
180  *   principal name.  May be %NULL (to only populate @outlen).
181  * @outlen: pointer to length of @out on output, excluding terminating
182  *   null.  May be %NULL (to only populate @out).
183  *
184  * Represent principal name in ASN.1 structure as null-terminated
185  * string.  The string is allocated by this function, and it is the
186  * responsibility of the caller to deallocate it.  Note that the
187  * output length @outlen does not include the terminating null.
188  *
189  * Return value: Returns SHISHI_OK if successful.
190  **/
191 int
shishi_principal_name(Shishi * handle,Shishi_asn1 namenode,const char * namefield,char ** out,size_t * outlen)192 shishi_principal_name (Shishi * handle,
193 		       Shishi_asn1 namenode,
194 		       const char *namefield, char **out, size_t * outlen)
195 {
196   char *format;
197   size_t i, j, n;
198   char *name = NULL;
199   size_t namelen = 0;
200   int res;
201 
202   asprintf (&format, "%s.name-string", namefield);
203   res = shishi_asn1_number_of_elements (handle, namenode, format, &n);
204   free (format);
205   if (res != SHISHI_OK)
206     return res;
207 
208   for (i = 1; i <= n; i++)
209     {
210       char *tmp;
211       size_t tmplen;
212       size_t safetmplen;
213 
214       asprintf (&format, "%s.name-string.?%ld", namefield, i);
215       res = shishi_asn1_read (handle, namenode, format, &tmp, &tmplen);
216       free (format);
217       if (res != SHISHI_OK)
218 	return res;
219 
220       safetmplen = tmplen;
221       for (j = 0; j < tmplen; j++)
222 	if (tmp[j] == '@' || tmp[j] == '/' || tmp[j] == '\\')
223 	  safetmplen++;
224       if (i < n)
225 	safetmplen++;
226 
227       name = xrealloc (name, namelen + safetmplen);
228 
229       for (j = 0; j < tmplen; j++)
230 	{
231 	  if (tmp[j] == '@' || tmp[j] == '/' || tmp[j] == '\\')
232 	    name[namelen++] = '\\';
233 	  name[namelen++] = tmp[j];
234 	}
235 
236       if (i < n)
237 	name[namelen++] = '/';
238 
239       free (tmp);
240     }
241 
242   name = xrealloc (name, namelen + 1);
243   name[namelen] = '\0';
244 
245   if (out)
246     *out = name;
247   else
248     free (name);
249   if (outlen)
250     *outlen = namelen;
251 
252   return SHISHI_OK;
253 }
254 
255 /**
256  * shishi_principal_name_realm:
257  * @handle: Shishi library handle created by shishi_init().
258  * @namenode: ASN.1 structure with principal name in @namefield.
259  * @namefield: name of field in @namenode containing principal name.
260  * @realmnode: ASN.1 structure with principal realm in @realmfield.
261  * @realmfield: name of field in @realmnode containing principal realm.
262  * @out: pointer to newly allocated null terminated string containing
263  *   principal name.  May be %NULL (to only populate @outlen).
264  * @outlen: pointer to length of @out on output, excluding terminating
265  *   null.  May be %NULL (to only populate @out).
266  *
267  * Represent principal name and realm in ASN.1 structure as
268  * null-terminated string.  The string is allocated by this function.
269  * It is the responsibility of the caller to deallocate it.  Note
270  * that the output length @outlen does not include the terminating
271  * null character.
272  *
273  * Return value: Returns SHISHI_OK if successful.
274  **/
275 int
shishi_principal_name_realm(Shishi * handle,Shishi_asn1 namenode,const char * namefield,Shishi_asn1 realmnode,const char * realmfield,char ** out,size_t * outlen)276 shishi_principal_name_realm (Shishi * handle,
277 			     Shishi_asn1 namenode,
278 			     const char *namefield,
279 			     Shishi_asn1 realmnode,
280 			     const char *realmfield,
281 			     char **out, size_t * outlen)
282 {
283   char *tmp;
284   size_t tmplen;
285   int rc;
286 
287   rc = shishi_principal_name (handle, namenode, namefield, &tmp, &tmplen);
288   if (rc != SHISHI_OK)
289     return rc;
290 
291   if (realmnode == NULL && realmfield)
292     {
293       size_t realmfieldlen = strlen (realmfield);
294 
295       tmp = xrealloc (tmp, tmplen + 1 + realmfieldlen + 1);
296 
297       tmp[tmplen] = '@';
298       memcpy (tmp + tmplen + 1, realmfield, realmfieldlen);
299 
300       tmplen += 1 + realmfieldlen;
301 
302       tmp[tmplen] = '\0';
303     }
304   else if (realmnode != NULL)
305     {
306       char *realm;
307       size_t realmlen;
308 
309       rc = shishi_asn1_read (handle, realmnode, realmfield,
310 			     &realm, &realmlen);
311       if (rc != SHISHI_OK)
312 	{
313 	  free (tmp);
314 	  return rc;
315 	}
316 
317       tmp = xrealloc (tmp, tmplen + 1 + realmlen + 1);
318 
319       tmp[tmplen] = '@';
320       memcpy (tmp + tmplen + 1, realm, realmlen);
321 
322       tmplen += 1 + realmlen;
323 
324       tmp[tmplen] = '\0';
325 
326       free (realm);
327     }
328 
329   *out = tmp;
330   if (outlen)
331     *outlen = tmplen;
332 
333   return SHISHI_OK;
334 }
335 
336 /**
337  * shishi_principal_name_set:
338  * @handle: shishi handle as allocated by shishi_init().
339  * @namenode: ASN.1 structure with principal in @namefield.
340  * @namefield: name of field in @namenode containing principal name.
341  * @name_type: type of principal, see Shishi_name_type, usually
342  *             SHISHI_NT_UNKNOWN.
343  * @name: null-terminated input array with principal name.
344  *
345  * Set the given principal name field to the given name.
346  *
347  * Return value: Returns SHISHI_OK if successful.
348  **/
349 int
shishi_principal_name_set(Shishi * handle,Shishi_asn1 namenode,const char * namefield,Shishi_name_type name_type,const char * name[])350 shishi_principal_name_set (Shishi * handle,
351 			   Shishi_asn1 namenode,
352 			   const char *namefield,
353 			   Shishi_name_type name_type, const char *name[])
354 {
355   int res;
356   char *asn1name;
357   int i;
358 
359   asprintf (&asn1name, "%s.name-type", namefield);
360   res = shishi_asn1_write_int32 (handle, namenode, asn1name, name_type);
361   free (asn1name);
362   if (res != SHISHI_OK)
363     return res;
364 
365   asprintf (&asn1name, "%s.name-string", namefield);
366   res = shishi_asn1_write (handle, namenode, asn1name, NULL, 0);
367   free (asn1name);
368   if (res != SHISHI_OK)
369     return res;
370 
371   i = 1;
372   while (name[i - 1])
373     {
374       asprintf (&asn1name, "%s.name-string", namefield);
375       res = shishi_asn1_write (handle, namenode, asn1name, "NEW", 1);
376       free (asn1name);
377       if (res != SHISHI_OK)
378 	return res;
379 
380       asprintf (&asn1name, "%s.name-string.?%d", namefield, i);
381       res = shishi_asn1_write (handle, namenode, asn1name, name[i - 1], 0);
382       free (asn1name);
383       if (res != SHISHI_OK)
384 	return res;
385 
386       i++;
387     }
388 
389   return SHISHI_OK;
390 }
391 
392 /**
393  * shishi_principal_set:
394  * @handle: shishi handle as allocated by shishi_init().
395  * @namenode: ASN.1 structure with principal in @namefield.
396  * @namefield: name of field in @namenode containing principal name.
397  * @name: null-terminated string with principal name in RFC 1964 form.
398  *
399  * Set principal name field in an ASN.1 structure to the given name.
400  *
401  * Return value: Returns SHISHI_OK if successful.
402  **/
403 int
shishi_principal_set(Shishi * handle,Shishi_asn1 namenode,const char * namefield,const char * name)404 shishi_principal_set (Shishi * handle,
405 		      Shishi_asn1 namenode,
406 		      const char *namefield, const char *name)
407 {
408   char *tmpname;
409   const char **namebuf;
410   char *tokptr = NULL;
411   int res;
412   int i;
413 
414   tmpname = xstrdup (name);
415   namebuf = xmalloc (sizeof (*namebuf));
416 
417   for (i = 0;
418        (namebuf[i] = strtok_r (i == 0 ? tmpname : NULL, "/", &tokptr)); i++)
419     {
420       namebuf = xrealloc (namebuf, (i + 2) * sizeof (*namebuf));
421     }
422 
423   res = shishi_principal_name_set (handle, namenode, namefield,
424 				   SHISHI_NT_UNKNOWN, namebuf);
425   free (namebuf);
426   free (tmpname);
427   if (res != SHISHI_OK)
428     {
429       shishi_error_printf (handle, _("Could not set principal name: %s\n"),
430 			   shishi_strerror (res));
431       return res;
432     }
433 
434   return SHISHI_OK;
435 }
436 
437 /**
438  * shishi_derive_default_salt:
439  * @handle: shishi handle as allocated by shishi_init().
440  * @name: principal name of user.
441  * @salt: output variable with newly allocated salt string.
442  *
443  * Derive the default salt from a principal.  The default salt is the
444  * concatenation of the decoded realm and the principal.
445  *
446  * Return value: Return SHISHI_OK if successful.
447  **/
448 int
shishi_derive_default_salt(Shishi * handle,const char * name,char ** salt)449 shishi_derive_default_salt (Shishi * handle, const char *name, char **salt)
450 {
451   char *principal;
452   char *realm;
453   char *p;
454   int rc;
455 
456   /* XXX also deal with escaped /'s. */
457 
458   rc = shishi_parse_name (handle, name, &principal, &realm);
459   if (rc != SHISHI_OK)
460     return rc;
461 
462   if (!principal || !realm)
463     {
464       free (realm);
465       free (principal);
466       return SHISHI_INVALID_PRINCIPAL_NAME;
467     }
468 
469   while ((p = strchr (principal, '/')))
470     memmove (p, p + 1, strlen (p));
471 
472   *salt = xasprintf ("%s%s", realm, principal);
473 
474   free (realm);
475   free (principal);
476 
477   return SHISHI_OK;
478 }
479 
480 /**
481  * shishi_server_for_local_service:
482  * @handle: shishi handle as allocated by shishi_init().
483  * @service: null terminated string with name of service, e.g., "host".
484  *
485  * Construct a service principal (e.g., "imap/yxa.extuno.com") based
486  * on supplied service name (i.e., "imap") and the system's hostname as
487  * returned by hostname() (i.e., "yxa.extundo.com").  The string must
488  * be deallocated by the caller.
489  *
490  * Return value: Return newly allocated service name string.
491  **/
492 char *
shishi_server_for_local_service(Shishi * handle,const char * service)493 shishi_server_for_local_service (Shishi * handle, const char *service)
494 {
495   char *hostname;
496   char *server;
497 
498   hostname = xgethostname ();
499 
500   asprintf (&server, "%s/%s", service, hostname);
501 
502   free (hostname);
503 
504   return server;
505 }
506