1 /* zxiduser.c  -  Handwritten functions for SP user local account management
2  * Copyright (c) 2012 Synergetics NV (sampo@synergetics.be), All Rights Reserved.
3  * Copyright (c) 2009-2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
4  * Copyright (c) 2007-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
5  * Author: Sampo Kellomaki (sampo@iki.fi)
6  * This is confidential unpublished proprietary source code of the author.
7  * NO WARRANTY, not even implied warranties. Contains trade secrets.
8  * Distribution prohibited unless authorized in writing.
9  * Licensed under Apache License 2.0, see file COPYING.
10  * $Id: zxiduser.c,v 1.18 2009-11-29 12:23:06 sampo Exp $
11  *
12  * 12.10.2007, created --Sampo
13  * 7.10.2008,  added documentation --Sampo
14  * 14.11.2009, added yubikey (yubico.com) support --Sampo
15  * 23.9.2010,  added delegation support --Sampo
16  * 1.9.2012,   distilled the authentication backend to an independent module zxpw.c --Sampo
17  */
18 
19 #include "platform.h"  /* for dirent.h */
20 
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <errno.h>
27 
28 #ifdef USE_OPENSSL
29 #include <openssl/des.h>
30 #endif
31 
32 #include "errmac.h"
33 #include "zxid.h"
34 #include "zxidutil.h"
35 #include "zxidconf.h"
36 #include "c/zx-sa-data.h"
37 
38 /*() Parse a line from .mni and form a NameID, unless there is mniptr */
39 
40 /* Called by:  zxid_check_fed, zxid_get_user_nameid */
zxid_parse_mni(zxid_conf * cf,char * buf,char ** pmniptr)41 zxid_nid* zxid_parse_mni(zxid_conf* cf, char* buf, char** pmniptr)
42 {
43   zxid_nid* nameid;
44   char* p;
45   char* idpent = 0;
46   char* spqual = 0;
47   char* nid = 0;
48   char* mniptr = 0;
49 
50   p = strchr(buf, '|');
51   if (p) {
52     *p = 0;
53     idpent = ++p;
54     p = strchr(p, '|');
55     if (p) {
56       *p = 0;
57       spqual = ++p;
58       p = strchr(p, '|');
59       if (p) {
60 	*p = 0;
61 	nid = ++p;
62 	p = strchr(p, '|');
63 	if (p) {
64 	  *p = 0;
65 	  mniptr = ++p;
66 	  p = strchr(p, '|');
67 	  if (p)
68 	    *p = 0;
69 	}
70       }
71     }
72   }
73 
74   if (mniptr && *mniptr) {
75     if (pmniptr)
76       *pmniptr = mniptr;
77     return 0;
78   }
79 
80   nameid = zx_NEW_sa_NameID(cf->ctx,0);
81   if (spqual && *spqual) nameid->SPNameQualifier = zx_dup_attr(cf->ctx, &nameid->gg, zx_SPNameQualifier_ATTR, spqual);
82   if (idpent && *idpent) nameid->NameQualifier   = zx_dup_attr(cf->ctx, &nameid->gg, zx_NameQualifier_ATTR, idpent);
83   if (*buf)              nameid->Format = zx_dup_attr(cf->ctx, &nameid->gg, zx_Format_ATTR, buf);
84   if (nid && *nid)       zx_add_content(cf->ctx, &nameid->gg, zx_dup_str(cf->ctx, nid));
85   return nameid;
86 }
87 
88 /*() Formulate NameID based directory name for the user. qualif is usually
89  * the IdP Entity ID. It is important to separate between same nid
90  * issued by different IdP. The result is "returned" by modifying
91  * sha1_name buffer, which MUST be at least 28 characters long. */
92 
93 /* Called by:  zxid_get_user_nameid, zxid_put_user, zxid_ses_to_pool x2, zxid_user_change_nameid */
zxid_user_sha1_name(zxid_conf * cf,struct zx_str * qualif,struct zx_str * nid,char * sha1_name)94 void zxid_user_sha1_name(zxid_conf* cf, struct zx_str* qualif, struct zx_str* nid, char* sha1_name)
95 {
96   struct zx_str* ss;
97   if (!nid) {
98     ZERO(sha1_name, 28);
99     return;
100   }
101   if (qualif) {
102     ss = zx_strf(cf->ctx, "%.*s|%.*s", qualif->len, qualif->s, nid->len, nid->s);
103     sha1_safe_base64(sha1_name, ss->len, ss->s);
104     zx_str_free(cf->ctx, ss);
105   } else {
106     sha1_safe_base64(sha1_name, nid->len, nid->s);
107   }
108   sha1_name[27] = 0;
109 }
110 
111 /*() Locate user file using a NameID, which may be old or current. If old,
112  * chase the MNIptr fields until current is found. Mainly used to support MNI. */
113 
114 /* Called by:  zxid_sp_mni_redir, zxid_sp_mni_soap, zxid_sp_slo_redir, zxid_sp_slo_soap */
zxid_get_user_nameid(zxid_conf * cf,zxid_nid * oldnid)115 zxid_nid* zxid_get_user_nameid(zxid_conf* cf, zxid_nid* oldnid)
116 {
117   char sha1_name[28];
118   char* buf;
119   char* mniptr;
120   int iter = 1000;
121   zxid_nid* nameid;
122 
123   if (!cf->user_local)
124     return oldnid;
125 
126   zxid_user_sha1_name(cf, &oldnid->NameQualifier->g, ZX_GET_CONTENT(oldnid), sha1_name);
127   buf = ZX_ALLOC(cf->ctx, ZXID_MAX_USER);
128   mniptr = sha1_name;
129 
130   while (--iter && mniptr && *mniptr) {
131     read_all(ZXID_MAX_USER, buf, (const char*)__FUNCTION__, 1, "%s" ZXID_USER_DIR "%s/.mni", cf->cpath, mniptr);
132     nameid = zxid_parse_mni(cf, buf, &mniptr);
133     if (nameid)
134       return nameid;
135     if (!mniptr || !strcmp(mniptr, sha1_name)) {
136       ERR("Infinite loop in MNI changed NameIDs in user database mniptr(%s) iter(%d)", STRNULLCHK(mniptr), iter);
137       return 0;
138     }
139   }
140   ERR("Too many mniptr indirections for oldnid(%.*s)", ZX_GET_CONTENT_LEN(oldnid), ZX_GET_CONTENT_S(oldnid));
141   return 0;
142 }
143 
144 /*() Change a NameID to newnym. Old NameID's user entry is rewritten to have mniptr */
145 
146 /* Called by:  zxid_mni_do */
zxid_user_change_nameid(zxid_conf * cf,zxid_nid * oldnid,struct zx_str * newnym)147 void zxid_user_change_nameid(zxid_conf* cf, zxid_nid* oldnid, struct zx_str* newnym)
148 {
149   char sha1_name[28];
150   zxid_user_sha1_name(cf, &oldnid->NameQualifier->g, newnym, sha1_name);
151   zxid_put_user(cf, &oldnid->Format->g, &oldnid->NameQualifier->g, &oldnid->SPNameQualifier->g, newnym, 0);
152   zxid_put_user(cf, &oldnid->Format->g, &oldnid->NameQualifier->g, &oldnid->SPNameQualifier->g, ZX_GET_CONTENT(oldnid), sha1_name);
153 }
154 
155 /*() Create new user object in file system. Will create user diretory (but not
156  * its subdirectories).
157  * See also zxid_ses_to_pool() */
158 
159 /* Called by:  zxid_sp_sso_finalize, zxid_user_change_nameid x2, zxid_wsp_validate_env */
zxid_put_user(zxid_conf * cf,struct zx_str * nidfmt,struct zx_str * idpent,struct zx_str * spqual,struct zx_str * idpnid,char * mniptr)160 int zxid_put_user(zxid_conf* cf, struct zx_str* nidfmt, struct zx_str* idpent, struct zx_str* spqual, struct zx_str* idpnid, char* mniptr)
161 {
162   char sha1_name[28];
163   char dir[ZXID_MAX_BUF];
164   char* buf;
165 
166   if (!cf->user_local)
167     return 0;
168 
169   if (!idpnid) {
170     ERR("Missing NameID %p", idpent);
171     return 0;
172   }
173 
174   zxid_user_sha1_name(cf, idpent, idpnid, sha1_name);
175   name_from_path(dir, sizeof(dir), "%s" ZXID_USER_DIR "%s", cf->cpath, sha1_name);
176   if (MKDIR(dir, 0777) && errno != EEXIST) {
177     ERR("Creating user directory(%s) failed: %d %s; euid=%d egid=%d", dir, errno, STRERROR(errno), geteuid(), getegid());
178     return 0;
179   }
180 
181   buf = ZX_ALLOC(cf->ctx, ZXID_MAX_USER);
182   write_all_path_fmt("put_user", ZXID_MAX_USER, buf,
183 		     "%s" ZXID_USER_DIR "%s/.mni", cf->cpath, sha1_name,
184 		     "%.*s|%.*s|%.*s|%.*s|%s",
185 		     nidfmt?nidfmt->len:0, nidfmt?nidfmt->s:"",
186 		     idpent?idpent->len:0, idpent?idpent->s:"",
187 		     spqual?spqual->len:0, spqual?spqual->s:"",
188 		     idpnid->len, idpnid->s,
189 		     STRNULLCHK(mniptr));
190   ZX_FREE(cf->ctx, buf);
191   D("PUT USER idpnid(%.*s)", idpnid->len, idpnid->s);
192   return 1;
193 }
194 
195 static char* login_failed = "Login failed. Check username and password. Make sure you have an active local account. Or just try some other authentication method or another IdP.<p>";
196 
197 /*() Authenticate by a pairing code.
198  * Pairing code is generated by an external program such as idppairing.pl
199  * after user has logged in. Pairing code can be introduced in another device,
200  * such as mobile phone with limited keyboard, to authenticate the user based
201  * on original authentication to the pairing web site.
202  */
203 
zxid_check_mobile_pairing(zxid_conf * cf,zxid_cgi * cgi)204 static int zxid_check_mobile_pairing(zxid_conf* cf, zxid_cgi* cgi)
205 {
206   int len, secs;
207   char* uid;
208   char buf[1024];
209   len = read_all(sizeof(buf), (char*)buf, "pairing", 0, "%s" ZXID_PCODE_DIR "%s", cf->cpath, cgi->pcode);
210   if (len <= 0) {
211     ERR("Bad pairing pcode(%s)", cgi->pcode);
212     return 0;
213   }
214   secs = atoi(buf);
215   if (secs < time(0)) {
216     ERR("Bad pairing pcode(%s) buf(%s) expired=%d, now=%d", cgi->pcode, buf, secs, (int)time(0));
217     return 0;
218   }
219   uid = strchr(buf, ' ');
220   if (!uid) {
221     ERR("Bad pairing pcode(%s) buf(%s) uid not found", cgi->pcode, buf);
222     return 0;
223   }
224   ++uid;
225   D("Pairing OK pcode(%s) buf(%s) expired=%d, now=%d uid=%s", cgi->pcode, buf, secs, (int)time(0), uid);
226   cgi->uid = zx_dup_cstr(cf->ctx, uid);
227   snprintf(buf, sizeof(buf), "%s" ZXID_PCODE_DIR "%s", cf->cpath, cgi->pcode);
228   unlink(buf);
229   return 2;  /* *** what is good authentication context class for pairing? Password equivalent? */
230 }
231 
232 /*() Locally authenticate user. If successful, create a session.
233  * Expects to get username and password in cgi->au and cgi->ap
234  * respectively. User authetication is done against local database or
235  * by default using /var/zxid/uid/uid/.pw file. When filesystem
236  * backend is used, for safety reasons the uid (user) component can
237  * not have certain characters, such as slash (/) or sequences like "..".
238  * See also: zxpasswd.c
239  *
240  * return:: 0 on failure and sets cgi->err; 1 on success  */
241 
242 /* Called by:  zxid_idp_as_do, zxid_simple_idp_pw_authn, zxid_simple_idp_show_an */
zxid_pw_authn(zxid_conf * cf,zxid_cgi * cgi,zxid_ses * ses)243 int zxid_pw_authn(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses)
244 {
245   int an_level;
246   struct zx_str* ss;
247   struct zxid_cstr_list* ac;
248 
249   if (cgi->pcode) {
250     an_level = zxid_check_mobile_pairing(cf, cgi);
251   } else {
252     an_level = zx_password_authn(cf->cpath, cgi->uid, cgi->pw, cgi->pin, 0);
253   }
254   if (!an_level) {
255     cgi->err = login_failed;
256     return 0;
257   }
258 
259   /* Successful login. Establish session. */
260 
261   ZERO(ses, sizeof(zxid_ses));
262   ses->magic = ZXID_SES_MAGIC;
263   ses->an_instant = time(0);  /* This will be later used by AuthnStatement constructor. */
264 
265   for (ac = cf->issue_authnctx; ac && an_level > 0; ac = ac->n, --an_level) ;
266   if (!ac)
267     ac = cf->issue_authnctx;
268   ses->an_ctx = ac->s;
269 
270   /* Master session. Each pairwise SSO has its own to avoid correlation, see zxid_mk_an_stmt() */
271   ss = zxid_mk_id(cf, "MMSES", ZXID_ID_BITS);
272   ses->sesix = ss->s;
273   ZX_FREE(cf->ctx, ss);
274   ses->sid = cgi->sid = ses->sesix;
275   ses->uid = cgi->uid;
276   zxid_put_ses(cf, ses);
277   if (cf->ses_cookie_name && *cf->ses_cookie_name) {
278     ses->setcookie = zx_alloc_sprintf(cf->ctx, 0, "%s=%s; path=/%s%s",
279 				      cf->ses_cookie_name, ses->sid,
280 				      cgi->mob?"; Max-Age=15481800":"",
281 				      ONE_OF_2(cf->burl[4], 's', 'S')?"; secure; HttpOnly":"; HttpOnly");
282     ses->cookie = zx_alloc_sprintf(cf->ctx, 0, "$Version=1; %s=%s",
283 				   cf->ses_cookie_name, ses->sid);
284   }
285   INFO("LOCAL LOGIN SUCCESSFUL. sid(%s) uid(%s)", cgi->sid, cgi->uid);
286   zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "K", "INEWSES", ses->sid, "uid(%s)", ses->uid);
287   if (cf->loguser)
288     zxlogusr(cf, ses->uid, 0,0,0,0,0,0,0, "N", "K", "INEWSES", ses->sid, "uid(%s)", ses->uid);
289   return 1;
290 }
291 
292 /* EOF  --  zxiduser.c */
293