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