1 /* zxidps.c  -  People Service
2  * Copyright (c) 2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3  * Copyright (c) 2010 Risaris Ltd, All Rights Reserved.
4  * Author: Sampo Kellomaki (sampo@iki.fi)
5  * This is confidential unpublished proprietary source code of the author.
6  * NO WARRANTY, not even implied warranties. Contains trade secrets.
7  * Distribution prohibited unless authorized in writing.
8  * Licensed under Apache License 2.0, see file COPYING.
9  * $Id: zxiddi.c,v 1.2 2009-11-24 23:53:40 sampo Exp $
10  *
11  * 16.9.2010, created --Sampo
12  *
13  * See also zxcall for client
14  * - liberty-idwsf-overview-v2.0.pdf sec 2.3 and 2.4 (pp.15-31) for use cases
15  * - liberty-idwsf-people-service-v1.0.pdf, sec 4.4 Elements Supporting Invitation (pp.53-57)
16  *
17  *  zxcot -e http://idp.tas3.pt:8081/zxididp?o=S 'People Svc' \
18  *    http://idp.tas3.pt:8081/zxididp?o=B urn:liberty:ps:2006-08 \
19  *  | zxcot -b /var/zxid/idpdimd
20  */
21 
22 #include "platform.h"  /* for dirent.h */
23 #include "errmac.h"
24 #include "zxid.h"
25 #include "zxidpriv.h"
26 #include "zxidutil.h"
27 #include "zxidconf.h"
28 #include "saml2.h"
29 #include "wsf.h"
30 #include "c/zx-const.h"
31 #include "c/zx-ns.h"
32 #include "c/zx-data.h"
33 
34 /* Called by:  zxid_psobj_dec, zxid_psobj_enc */
zxid_psobj_key_setup(zxid_conf * cf,struct zx_str * eid,char * symkey)35 static int zxid_psobj_key_setup(zxid_conf* cf, struct zx_str* eid, char* symkey)
36 {
37   if (!cf->psobj_symkey[0])
38     zx_get_symkey(cf, "psobj-enc.key", cf->psobj_symkey);
39   zx_raw_digest2(cf->ctx, symkey, "SHA1", strlen(cf->psobj_symkey), cf->psobj_symkey, eid->len, eid->s);
40   return 20;
41 }
42 
43 /*() Encrypt and safe_base64 encode psobj identifier for an entity.
44  *
45  * ObjectID (psobj) has particlar privacy threat in that several WSCs may
46  * see them and be able to correlate about the user that the object refers
47  * to (see brief discussion in sec 2.1.4 "<ObjectID> Element", ll.278-281,
48  * of [PeopleSvc].
49  *
50  * We adopt solution where psobj issued towards an entity (SP, WSC) is
51  * the psobj encrypted (AES-128-CBC) with key consisting of concatenation
52  * of secret (/var/zxid/pem/psobj-enc.key) known to ps server (i.e. the
53  * zxididp) and the Entity ID of the entity. */
54 
55 /* Called by:  zxid_mk_an_stmt, zxid_ps_addent_invite */
zxid_psobj_enc(zxid_conf * cf,struct zx_str * eid,const char * prefix,struct zx_str * psobj)56 struct zx_str* zxid_psobj_enc(zxid_conf* cf, struct zx_str* eid, const char* prefix, struct zx_str* psobj)
57 {
58   char* lim;
59   char symkey[20];
60   struct zx_str key;
61   struct zx_str* ss;
62   struct zx_str* rr;
63   int prefix_len = strlen(prefix);
64   zxid_psobj_key_setup(cf, eid, symkey);
65   key.len = 16;
66   key.s = symkey;
67   ss = zx_raw_cipher(cf->ctx, "AES-128-CBC", 1, &key, psobj->len, psobj->s, 16, 0);
68   if (!ss) {
69     ERR("Symmetric encryption failed %d", 0);
70     return 0;
71   }
72   rr = zx_new_len_str(cf->ctx, prefix_len+SIMPLE_BASE64_LEN(ss->len)+1);
73   strcpy(rr->s, prefix);
74   lim = base64_fancy_raw(ss->s, ss->len, rr->s+prefix_len, safe_basis_64, 1<<31, 0, "", '=');
75   *lim = 0;
76   rr->len = lim - rr->s;
77   zx_str_free(cf->ctx, ss);
78   return rr;
79 }
80 
81 /*() Decrypt psobj identifier from an entity. */
82 
83 /* Called by:  zxid_idp_slo_do, zxid_ps_addent_invite */
zxid_psobj_dec(zxid_conf * cf,struct zx_str * eid,const char * prefix,struct zx_str * psobj)84 struct zx_str* zxid_psobj_dec(zxid_conf* cf, struct zx_str* eid, const char* prefix, struct zx_str* psobj)
85 {
86   char* lim;
87   char symkey[20];
88   struct zx_str key;
89   struct zx_str* ss;
90   struct zx_str* rr;
91   int prefix_len = strlen(prefix);
92   if (!eid || !psobj || psobj->len < prefix_len) {
93     ERR("Null eid or psobj, or too short psobj %p", psobj);
94     return 0;
95   }
96   if (memcmp(prefix, psobj->s, prefix_len)) {
97     ERR("psobj(%.*s) does not match prefix(%s)", psobj->len, psobj->s, prefix);
98     return 0;
99   }
100   zxid_psobj_key_setup(cf, eid, symkey);
101   key.len = 16;
102   key.s = symkey;
103   rr = zx_new_len_str(cf->ctx, SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(psobj->len));
104   lim = unbase64_raw(psobj->s+prefix_len, psobj->s+psobj->len, rr->s, zx_std_index_64);
105   rr->len = lim - rr->s;
106   ss = zx_raw_cipher(cf->ctx, "AES-128-CBC", 0, &key, rr->len-16, rr->s+16, 16, rr->s);
107   zx_str_free(cf->ctx, rr);
108   return ss;
109 }
110 
111 /*() Render the linked list of delegated permissions to a string */
112 
113 /* Called by:  zxid_put_invite, zxid_put_psobj */
zxid_render_perms(zxid_conf * cf,struct zxid_perm * perms)114 char* zxid_render_perms(zxid_conf* cf, struct zxid_perm* perms)
115 {
116   int n, len = 0;
117   struct zxid_perm* perm;
118   char* ret;
119   char* p;
120 
121   /* Length computation phase */
122 
123   for (perm = perms; perm; perm = perm->n)
124     len += sizeof("perm: ")-1 + (perm->eid?perm->eid->len:0) + 1 + (perm->qs?perm->qs->len:0) + 1;
125 
126   ret = p = ZX_ALLOC(cf->ctx, len+1);
127 
128   /* Rendering phase */
129 
130   for (perm = perms; perm; perm = perm->n) {
131     n = sprintf(p, "perm: %.*s$%.*s\n",
132 		perm->eid?perm->eid->len:0, perm->eid?perm->eid->s:"",
133 		perm->qs?perm->qs->len:0,   perm->qs?perm->qs->s:"");
134     p += n;
135   }
136 
137   ASSERTOPI(p-ret, ==, len);
138   *p = 0; /* nul terminate */
139   return ret;
140 }
141 
142 /*() Render the linked list of invitation IDs to a string */
143 
144 /* Called by:  zxid_put_psobj x2 */
zxid_render_str_list(zxid_conf * cf,struct zx_str * strs,const char * attr_name)145 char* zxid_render_str_list(zxid_conf* cf, struct zx_str* strs, const char* attr_name)
146 {
147   int n, len = 0, atn_len = strlen(attr_name);
148   struct zx_str* str;
149   char* ret;
150   char* p;
151 
152   /* Length computation phase */
153 
154   for (str = strs; str; str = str->n)
155     len += atn_len + sizeof(": ")-1 + str->len + 1;
156 
157   ret = p = ZX_ALLOC(cf->ctx, len+1);
158 
159   /* Rendering phase */
160 
161   for (str = strs; str; str = str->n) {
162     n = sprintf(p, "%s: %.*s\n", attr_name, str->len, str->s);
163     p += n;
164   }
165 
166   ASSERTOPI(p-ret, ==, len);
167   *p = 0; /* nul terminate */
168   return ret;
169 }
170 
171 /*() Create new invitation in file system. */
172 
173 /* Called by:  zxid_ps_addent_invite */
zxid_put_invite(zxid_conf * cf,struct zxid_invite * inv)174 int zxid_put_invite(zxid_conf* cf, struct zxid_invite* inv)
175 {
176   char buf[ZXID_MAX_USER];
177   char invid_c[ZXID_MAX_USER];
178   char* perms = zxid_render_perms(cf, inv->perms);
179   memcpy(invid_c, inv->invid->s, MIN(inv->invid->len, sizeof(invid_c)-1));
180   invid_c[sizeof(invid_c)-1] = 0;
181 
182   write_all_path_fmt("put_inv", ZXID_MAX_USER, buf,
183 		     "%s" ZXID_INV_DIR "%s", cf->cpath, invid_c,
184 		     "dn: invid=%.*s\ninvid: %.*s\nuid: %s\ndesc: %.*s\npsobj: %.*s\nps2spredir: %.*s\nmaxusage: %d\nusage: %d\nstarts: %s\nexpires: %s\n%s\n\n",
185 		     inv->invid->len, inv->invid->s,
186 		     inv->invid->len, inv->invid->s,
187 		     inv->uid,
188 		     inv->desc?inv->desc->len:0, inv->desc?inv->desc->s:"",
189 		     inv->psobj?inv->psobj->len:0, inv->psobj?inv->psobj->s:"",
190 		     inv->ps2spredir?inv->ps2spredir->len:0, inv->ps2spredir?inv->ps2spredir->s:"",
191 		     inv->maxusage,
192 		     inv->usage,
193 		     zxid_date_time(cf, inv->starts),
194 		     zxid_date_time(cf, inv->expires),
195 		     STRNULLCHK(perms));
196   D("PUT INVITATION invid(%s)", invid_c);
197   return 1;
198 }
199 
200 /*() Create new People Service Object in file system. */
201 
202 /* Called by:  zxid_ps_addent_invite */
zxid_put_psobj(zxid_conf * cf,struct zxid_psobj * obj)203 int zxid_put_psobj(zxid_conf* cf, struct zxid_psobj* obj)
204 {
205   char* buf = ZX_ALLOC(cf->ctx, ZXID_MAX_USER);
206   char* children = 0;  /* *** groups and children not supported yet. */
207   char* tags = zxid_render_str_list(cf, obj->invids, "tag");
208   char* invids = zxid_render_str_list(cf, obj->invids, "invid");
209   char* perms = zxid_render_perms(cf, obj->perms);
210   obj->mod_secs = time(0);
211 
212   write_all_path_fmt("put_psobj", ZXID_MAX_USER, buf,
213 		     "%s" ZXID_UID_DIR "%s", cf->cpath, obj->uid,
214 		     "dn: psobj=%.*s,uid=%s\npsobj: %.*s\nowner: %s\nidpnid: %.*s\ndispname: %.*s\nnodetype: %d\ncreated: %s\nmodified: %s\n%s%s%s%s\n\n",
215 		     obj->psobj->len, obj->psobj->s, obj->uid,
216 		     obj->psobj->len, obj->psobj->s, obj->uid,
217 		     obj->idpnid?obj->idpnid->len:0, obj->idpnid?obj->idpnid->s:"",
218 		     obj->dispname?obj->dispname->len:0, obj->dispname?obj->dispname->s:"",  /* *** Should really support multiple */
219 		     obj->nodetype,
220 		     zxid_date_time(cf, obj->create_secs),
221 		     zxid_date_time(cf, obj->mod_secs),
222 		     STRNULLCHK(children),
223 		     STRNULLCHK(tags),
224 		     STRNULLCHK(invids),
225 		     STRNULLCHK(perms));
226   ZX_FREE(cf->ctx, buf);
227   D("PUT PSOBJ(%.*s)", obj->psobj->len, obj->psobj->s);
228   return 1;
229 }
230 
231 /*() Populate psobj from LDIF. Parse LDIF format and insert attributes to struct.
232  * The input is temporarily modified and then restored. Do not pass const string. */
233 
234 /* Called by: */
zxid_parse_psobj(zxid_conf * cf,struct zxid_psobj * obj,char * p,const char * lk)235 int zxid_parse_psobj(zxid_conf* cf, struct zxid_psobj* obj, char* p, const char* lk)
236 {
237   char* name;
238   char* val;
239   char* q;
240   struct zx_str* ss;
241   struct zxid_perm* perm;
242 
243   for (; p; ++p) {
244     name = p;
245     p = strstr(p, ": ");
246     if (!p)
247       break;
248     *p = 0;
249     val = p+2;
250     p = strchr(val, '\n');  /* *** parsing LDIF is fragile if values are multiline */
251     if (p)
252       *p = 0;
253 
254     D("%s: ATTR(%s)=VAL(%s)", lk, name, val);
255 
256     switch (name[0]) {
257     case 'd':
258       if (!strcmp(name, "dn"))
259 	goto next;
260       if (!strcmp(name, "dispname")) {
261 	obj->dispname = zx_dup_str(cf->ctx, val);
262 	goto next;
263       }
264       break;
265     case 'i':
266       if (!strcmp(name, "idpnid")) {
267 	obj->idpnid = zx_dup_str(cf->ctx, val);
268 	goto next;
269       }
270       if (!strcmp(name, "invid")) {
271 	ss = zx_dup_str(cf->ctx, val);
272 	ss->n = obj->invids;
273 	obj->invids = ss;
274 	goto next;
275       }
276       break;
277     case 'p':
278       if (!strcmp(name, "psobj")) {
279 	obj->psobj = zx_dup_str(cf->ctx, val);
280 	goto next;
281       }
282       if (!strcmp(name, "psobjref")) {
283 	ERR("%s: *** Child objects not yet supported (%s: %s)", lk, name, val);
284 	/*obj->child = zx_dup_str(cf->ctx, val); *** */
285 	goto next;
286       }
287       if (!strcmp(name, "perm")) {
288 	perm = ZX_ZALLOC(cf->ctx, struct zxid_perm);
289 	q = strchr(val, '$');
290 	if (q) {
291 	  perm->eid = zx_dup_len_str(cf->ctx, q-val, val);
292 	  perm->qs  = zx_dup_str(cf->ctx, q);
293 	} else
294 	  perm->eid = zx_dup_str(cf->ctx, val);
295 	perm->n = obj->perms;
296 	obj->perms = perm;
297 	goto next;
298       }
299       break;
300     case 't':
301       if (!strcmp(name, "tag")) {
302 	ss = zx_dup_str(cf->ctx, val);
303 	ss->n = obj->tags;
304 	obj->tags = ss;
305 	goto next;
306       }
307       break;
308     case 'u':
309       if (!strcmp(name, "uid")) {
310 	obj->uid = zx_dup_cstr(cf->ctx, val);
311 	goto next;
312       }
313       break;
314     }
315     ERR("%s: Unknown name(%s) val(%s) in psobj LDIF file. Ignored.", lk, name, val);
316 
317   next:
318     val[-2] = ':'; /* restore */
319     if (p)
320       *p = '\n';
321     else
322       break;
323   }
324   return 1;
325 }
326 
327 /*() Populate invitation from LDIF. Parse LDIF format and insert attributes to struct.
328  * The input is temporarily modified and then restored. Do not pass const string. */
329 
330 /* Called by:  zxid_ps_accept_invite, zxid_ps_finalize_invite */
zxid_parse_invite(zxid_conf * cf,struct zxid_invite * inv,char * p,const char * lk)331 int zxid_parse_invite(zxid_conf* cf, struct zxid_invite* inv, char* p, const char* lk)
332 {
333   char* name;
334   char* val;
335   char* q;
336   struct zxid_perm* perm;
337 
338   for (; p; ++p) {
339     name = p;
340     p = strstr(p, ": ");
341     if (!p)
342       break;
343     *p = 0;
344     val = p+2;
345     p = strchr(val, '\n');  /* *** parsing LDIF is fragile if values are multiline */
346     if (p)
347       *p = 0;
348 
349     D("%s: ATTR(%s)=VAL(%s)", lk, name, val);
350 
351     switch (name[0]) {
352     case 'd':
353       if (!strcmp(name, "dn"))
354 	goto next;
355       if (!strcmp(name, "desc")) {
356 	inv->desc = zx_dup_str(cf->ctx, val);
357 	goto next;
358       }
359       break;
360     case 'e':
361       if (!strcmp(name, "expires")) {
362 	inv->expires = zx_date_time_to_secs(val);
363 	goto next;
364       }
365       break;
366     case 'i':
367       if (!strcmp(name, "invid")) {
368 	inv->invid = zx_dup_str(cf->ctx, val);
369 	goto next;
370       }
371       break;
372     case 'm':
373       if (!strcmp(name, "maxusage")) {
374 	sscanf(val, "%i", &inv->maxusage);
375 	goto next;
376       }
377       break;
378     case 'p':
379       if (!strcmp(name, "psobj")) {
380 	inv->psobj = zx_dup_str(cf->ctx, val);
381 	goto next;
382       }
383       if (!strcmp(name, "ps2spredir")) {
384 	inv->ps2spredir = zx_dup_str(cf->ctx, val);
385 	goto next;
386       }
387       if (!strcmp(name, "perm")) {
388 	perm = ZX_ZALLOC(cf->ctx, struct zxid_perm);
389 	q = strchr(val, '$');
390 	if (q) {
391 	  perm->eid = zx_dup_len_str(cf->ctx, q-val, val);
392 	  perm->qs  = zx_dup_str(cf->ctx, q);
393 	} else
394 	  perm->eid = zx_dup_str(cf->ctx, val);
395 	perm->n = inv->perms;
396 	inv->perms = perm;
397 	goto next;
398       }
399       break;
400     case 's':
401       if (!strcmp(name, "starts")) {
402 	inv->starts = zx_date_time_to_secs(val);
403 	goto next;
404       }
405       break;
406     case 'u':
407       if (!strcmp(name, "uid")) {
408 	inv->uid = zx_dup_cstr(cf->ctx, val);
409 	goto next;
410       }
411       if (!strcmp(name, "usage")) {
412 	sscanf(val, "%i", &inv->usage);
413 	goto next;
414       }
415       break;
416     }
417     ERR("%s: Unknown name(%s) val(%s) in invite LDIF file. Ignored.", lk, name, val);
418 
419   next:
420     val[-2] = ':'; /* restore */
421     if (p)
422       *p = '\n';
423     else
424       break;
425   }
426   return 1;
427 }
428 
429 /*() Accept an invitation. Process a URL of form https://idp.tas3.pt/zxididp?o=D&inv=i123431
430  * Both logged in and not yet logged in cases are possible. */
431 
432 /* Called by:  zxid_simple_no_ses_cf, zxid_simple_ses_active_cf */
zxid_ps_accept_invite(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,int * res_len,int auto_flags)433 char* zxid_ps_accept_invite(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, int* res_len, int auto_flags)
434 {
435   int now = time(0);
436   struct zxid_invite inv;
437   char buf[ZXID_MAX_BUF];
438   int got = read_all(sizeof(buf), buf, "accept_invite", 1, "%s" ZXID_INV_DIR "%s",cf->cpath,cgi->inv);
439   if (!got) {
440     ERR("Invitation not found(%s)", cgi->inv);
441     cgi->err = "Invitation not found.";
442     return zxid_simple_show_err(cf, cgi, res_len, auto_flags);
443   }
444   zxid_parse_invite(cf, &inv, buf, "accept_invite");
445   if (inv.maxusage <= inv.usage) {
446     ERR("Invitation(%s) has already been used (max_usage=%d, usage=%d)", cgi->inv, inv.maxusage, inv.usage);
447     cgi->err = "Invitation has already been used.";
448     return zxid_simple_show_err(cf, cgi, res_len, auto_flags);
449   }
450   if (inv.starts > now) {
451     ERR("Invitation(%s) is not active yet (starts=%d, now=%d)", cgi->inv, inv.starts, now);
452     cgi->err = "Invitation is not active yet.";
453     return zxid_simple_show_err(cf, cgi, res_len, auto_flags);
454   }
455   if (inv.expires <= now) {
456     ERR("Invitation(%s) has expired (expire=%d, now=%d)", cgi->inv, inv.expires, now);
457     cgi->err = "Invitation has expired.";
458     return zxid_simple_show_err(cf, cgi, res_len, auto_flags);
459   }
460 
461   cgi->msg = "This screen aims to complete the invitation process you started by clicking on the invitation link. Once completed, you will be redirected to the web site where the delegated resource is available. To complete invitation, People Service needs to authenticate you with your Identity Provider (IdP). Please choose your Identity Provider from popup menu (or enter the IdP URL in the space provided) and click Login.";
462 
463   cgi->rs = zx_alloc_sprintf(cf->ctx, 0, "o=G&inv=%s", cgi->inv);
464   return zxid_simple_show_idp_sel(cf, cgi, res_len, auto_flags);
465 }
466 
467 /*() Finalize an invitation. This function is invoked after zxid_ps_accept_invite() (o=D)
468  * when user is returning from IdP, by way of o=G placed in RelayState. */
469 
470 /* Called by:  zxid_simple_ses_active_cf */
zxid_ps_finalize_invite(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses,int * res_len,int auto_flags)471 char* zxid_ps_finalize_invite(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, int* res_len, int auto_flags)
472 {
473   int now = time(0);
474   struct zxid_invite inv;
475   char buf[ZXID_MAX_BUF];
476   int got = read_all(sizeof(buf), buf, "finalize_invite", 1, "%s" ZXID_INV_DIR "%s",cf->cpath,cgi->inv);
477   if (!got) {
478     ERR("Invitation not found(%s)", cgi->inv);
479     cgi->err = "Invitation not found.";
480     return zxid_simple_show_err(cf, cgi, res_len, auto_flags);
481   }
482   zxid_parse_invite(cf, &inv, buf, "accept_invite");
483   if (inv.maxusage <= inv.usage) {
484     ERR("Invitation(%s) has already been used (max_usage=%d, usage=%d)", cgi->inv, inv.maxusage, inv.usage);
485     cgi->err = "Invitation has already been used.";
486     return zxid_simple_show_err(cf, cgi, res_len, auto_flags);
487   }
488   if (inv.starts > now) {
489     ERR("Invitation(%s) is not active yet (starts=%d, now=%d)", cgi->inv, inv.starts, now);
490     cgi->err = "Invitation is not active yet.";
491     return zxid_simple_show_err(cf, cgi, res_len, auto_flags);
492   }
493   if (inv.expires <= now) {
494     ERR("Invitation(%s) has expired (expire=%d, now=%d)", cgi->inv, inv.expires, now);
495     cgi->err = "Invitation has expired.";
496     return zxid_simple_show_err(cf, cgi, res_len, auto_flags);
497   }
498 
499   cgi->msg = "This screen aims to complete the invitation process you started by clicking on the invitation link. Once completed, you will be redirected to the web site where the delegated resource is available. To complete invitation, People Service needs to authenticate you with your Identity Provider (IdP). Please choose your Identity Provider from popup menu (or enter the IdP URL in the space provided) and click Login.";
500 
501   cgi->rs = zx_alloc_sprintf(cf->ctx, 0, "o=G&inv=%s", cgi->inv);
502   return zxid_simple_show_idp_sel(cf, cgi, res_len, auto_flags);
503 }
504 
505 /*() Add an entity to buddy store and obtain an invitation token.
506  * In fact this call just adds the invitation as we can not know
507  * who will respond to the invitation. The actual object will be
508  * created in zxid_ps_accept_invite(). If object repesenting the
509  * user already exists, that will be reused. Otherwise new
510  * object will be created.
511  * The permissions are expected to be passed in as special
512  * tag "perm". */
513 
514 /* Called by:  zxid_sp_soap_dispatch */
zxid_ps_addent_invite(zxid_conf * cf,zxid_ses * ses,struct zx_ps_AddEntityRequest_s * req)515 struct zx_ps_AddEntityResponse_s* zxid_ps_addent_invite(zxid_conf* cf, zxid_ses* ses, struct zx_ps_AddEntityRequest_s* req)
516 {
517   struct zx_str* tag;
518   struct zx_ps_AddEntityResponse_s* resp = zx_NEW_ps_AddEntityResponse(cf->ctx,0);
519   struct zxid_invite* inv;
520   struct zxid_psobj* obj;
521   char uid[ZXID_MAX_USER];
522   D_INDENT("ps_inv: ");
523 
524   if (!req || !req->Object) {
525     ERR("Malformed request (%p): Object missing.", req);
526     resp->Status = zxid_mk_lu_Status(cf, &resp->gg, "Fail", 0, 0, 0);
527     D_DEDENT("ps_inv: ");
528     return resp;
529   }
530 
531   if (!zxid_idp_map_nid2uid(cf, sizeof(uid), uid, ses->tgtnameid, &resp->Status)) {
532     D_DEDENT("ps_inv: ");
533     return resp;
534   }
535 
536   inv = ZX_ZALLOC(cf->ctx, struct zxid_invite);
537   inv->invid = zxid_mk_id(cf, "i", 48);  /* What is secure and sufficient space? */
538   inv->uid = uid;
539   inv->maxusage = 1;
540   inv->starts = time(0);
541   inv->expires = time(0) + 86400 * 30;  /* *** make configurable (about a month) */
542   inv->ps2spredir = ZX_GET_CONTENT(req->PStoSPRedirectURL);
543   inv->perms = ZX_ZALLOC(cf->ctx, struct zxid_perm);
544   inv->perms->eid = ses->issuer;
545 
546   obj = ZX_ZALLOC(cf->ctx, struct zxid_psobj);
547 #if 0
548   obj->psobj = req->Object->ObjectID ? zxid_psobj_dec(cf, ses->issuer, "ZO", req->Object->ObjectID) : zxid_mk_id(cf, "o", 48);  /* What is secure and sufficient space? */
549 #else
550   if (req->Object->ObjectID) {
551     ERR("AddEntityRequest contained ObjectID(%.*s), but AddEntity is about creating new objects and the object IDs are assigned by People Service, not client. Ignoring ObjectID.", ZX_GET_CONTENT_LEN(req->Object->ObjectID), ZX_GET_CONTENT_S(req->Object->ObjectID));
552   }
553   obj->psobj = zxid_mk_id(cf, "o", 48);  /* What is secure and sufficient space? */
554 #endif
555   obj->uid = uid;
556   obj->dispname = ZX_GET_CONTENT(req->Object->DisplayName);
557   obj->tags = ZX_GET_CONTENT(req->Object->Tag);
558   obj->invids = inv->invid;
559   obj->create_secs = time(0);
560 
561   inv->psobj = obj->psobj;
562   zxid_put_invite(cf, inv);
563   zxid_put_psobj(cf, obj);
564 
565   /* The invitation URL will be processed by zxid_ps_accept_invite(), see above. */
566   resp->SPtoPSRedirectURL
567     = zx_new_str_elem(cf->ctx, &resp->gg, zx_ps_SPtoPSRedirectURL_ELEM,
568 		      zx_strf(cf->ctx, "%s?o=D&inv=%.*s", cf->burl, inv->invid->len, inv->invid->s));
569   resp->Object = zx_NEW_ps_Object(cf->ctx, &resp->gg);
570   resp->Object->ObjectID = zx_new_str_elem(cf->ctx, &resp->Object->gg, zx_ps_ObjectID_ELEM, zxid_psobj_enc(cf, ses->issuer, "ZO", obj->psobj));
571   resp->Object->DisplayName = zx_NEW_ps_DisplayName(cf->ctx, &resp->Object->gg);
572   zx_add_content(cf->ctx, &resp->Object->DisplayName->gg, obj->dispname);
573   resp->Object->DisplayName->Locale = zx_ref_attr(cf->ctx, &resp->Object->DisplayName->gg, zx_Locale_ATTR, "xx");  /* unknown locale */
574   for (tag = obj->tags; tag; tag = tag->n) {
575     resp->Object->Tag = zx_NEW_ps_Tag(cf->ctx, &resp->Object->gg);
576     zx_add_content(cf->ctx, &resp->Object->Tag->gg, tag);
577   }
578   resp->Object->NodeType = zx_ref_attr(cf->ctx, &resp->Object->gg, zx_NodeType_ATTR, obj->nodetype?PS_COL:PS_ENT);
579   resp->Object->CreatedDateTime = zxid_date_time_attr(cf, &resp->Object->gg, zx_CreatedDateTime_ATTR, obj->create_secs);
580   resp->Object->ModifiedDateTime = zxid_date_time_attr(cf, &resp->Object->gg, zx_TimeStamp_ATTR, obj->mod_secs);
581   resp->TimeStamp = resp->Object->CreatedDateTime;
582   resp->id = zx_ref_len_attr(cf->ctx, &resp->gg, zx_id_ATTR, inv->invid->len, inv->invid->s);  /* *** why is ID requred by schema at all? */
583   resp->Status = zxid_mk_lu_Status(cf, &resp->gg, "OK", 0, 0, 0);
584   zxlogwsp(cf, ses, "K", "PSINV", 0, "inv=%.*s", inv->invid->len, inv->invid->s);
585   D_DEDENT("ps_inv: ");
586   return resp;
587 }
588 
589 /*() Resolve an invitation token to identity */
590 
591 /* Called by:  zxid_sp_soap_dispatch */
zxid_ps_resolv_id(zxid_conf * cf,zxid_ses * ses,struct zx_ps_ResolveIdentifierRequest_s * req)592 struct zx_ps_ResolveIdentifierResponse_s* zxid_ps_resolv_id(zxid_conf* cf, zxid_ses* ses, struct zx_ps_ResolveIdentifierRequest_s* req)
593 {
594   struct zx_ps_ResolveIdentifierResponse_s* resp = zx_NEW_ps_ResolveIdentifierResponse(cf->ctx,0);
595   struct zx_ps_ResolveInput_s* inp;
596   //struct zx_ps_ResolveOutput_s* out;
597   int n_resolv = 0;
598   char uid[ZXID_MAX_USER];
599   D_INDENT("ps_resolv: ");
600 
601   //resp->ID = zxid_mk_id(cf, "DIR", ZXID_ID_BITS);
602 
603   if (!zxid_idp_map_nid2uid(cf, sizeof(uid), uid, ses->tgtnameid, &resp->Status)) {
604     D_DEDENT("ps_resolv: ");
605     return resp;
606   }
607 
608   for (inp = req->ResolveInput;
609        inp && inp->gg.g.tok == zx_ps_ResolveInput_ELEM;
610        inp = (struct zx_ps_ResolveInput_s*)inp->gg.g.n) {
611     // ***
612   }
613 
614   resp->Status = zxid_mk_lu_Status(cf, &resp->gg, "OK", 0, 0, 0);
615   zxlogwsp(cf, ses, "K", "PSRESOLVOK", 0, "n=%d", n_resolv);
616   D_DEDENT("ps_resolv: ");
617   return resp;
618 }
619 
620 /* EOF  --  zxidps.c */
621