1/*
2   Unix SMB/CIFS implementation.
3   session handling for utmp and PAM
4
5   Copyright (C) tridge@samba.org       2001
6   Copyright (C) abartlet@samba.org     2001
7   Copyright (C) Gerald (Jerry) Carter  2006
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 3 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21*/
22
23/* a "session" is claimed when we do a SessionSetupX operation
24   and is yielded when the corresponding vuid is destroyed.
25
26   sessions are used to populate utmp and PAM session structures
27*/
28
29#include "includes.h"
30#include "smbd/globals.h"
31
32/********************************************************************
33********************************************************************/
34
35static struct db_context *session_db_ctx(void)
36{
37	if (session_db_ctx_ptr)
38		return session_db_ctx_ptr;
39
40	session_db_ctx_ptr = db_open(NULL, lock_path("sessionid.tdb"), 0,
41				     TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
42				     O_RDWR | O_CREAT, 0644);
43	return session_db_ctx_ptr;
44}
45
46bool session_init(void)
47{
48	if (session_db_ctx() == NULL) {
49		DEBUG(1,("session_init: failed to open sessionid tdb\n"));
50		return False;
51	}
52
53	return True;
54}
55
56/********************************************************************
57 called when a session is created
58********************************************************************/
59
60bool session_claim(user_struct *vuser)
61{
62	TDB_DATA key, data;
63	int i = 0;
64	struct sessionid sessionid;
65	struct server_id pid = procid_self();
66	fstring keystr;
67	const char * hostname;
68	struct db_context *ctx;
69	struct db_record *rec;
70	NTSTATUS status;
71	char addr[INET6_ADDRSTRLEN];
72
73	vuser->session_keystr = NULL;
74
75	/* don't register sessions for the guest user - its just too
76	   expensive to go through pam session code for browsing etc */
77	if (vuser->server_info->guest) {
78		return True;
79	}
80
81	if (!(ctx = session_db_ctx())) {
82		return False;
83	}
84
85	ZERO_STRUCT(sessionid);
86
87	data.dptr = NULL;
88	data.dsize = 0;
89
90	if (lp_utmp()) {
91
92		for (i=1;i<MAX_SESSION_ID;i++) {
93
94			/*
95			 * This is very inefficient and needs fixing -- vl
96			 */
97
98			struct server_id sess_pid;
99
100			snprintf(keystr, sizeof(keystr), "ID/%d", i);
101			key = string_term_tdb_data(keystr);
102
103			rec = ctx->fetch_locked(ctx, NULL, key);
104
105			if (rec == NULL) {
106				DEBUG(1, ("Could not lock \"%s\"\n", keystr));
107				return False;
108			}
109
110			if (rec->value.dsize != sizeof(sessionid)) {
111				DEBUG(1, ("Re-using invalid record\n"));
112				break;
113			}
114
115			memcpy(&sess_pid,
116			       ((char *)rec->value.dptr)
117			       + offsetof(struct sessionid, pid),
118			       sizeof(sess_pid));
119
120			if (!process_exists(sess_pid)) {
121				DEBUG(5, ("%s has died -- re-using session\n",
122					  procid_str_static(&sess_pid)));
123				break;
124			}
125
126			TALLOC_FREE(rec);
127		}
128
129		if (i == MAX_SESSION_ID) {
130			SMB_ASSERT(rec == NULL);
131			DEBUG(1,("session_claim: out of session IDs "
132				 "(max is %d)\n", MAX_SESSION_ID));
133			return False;
134		}
135
136		snprintf(sessionid.id_str, sizeof(sessionid.id_str),
137			 SESSION_UTMP_TEMPLATE, i);
138	} else
139	{
140		snprintf(keystr, sizeof(keystr), "ID/%s/%u",
141			 procid_str_static(&pid), vuser->vuid);
142		key = string_term_tdb_data(keystr);
143
144		rec = ctx->fetch_locked(ctx, NULL, key);
145
146		if (rec == NULL) {
147			DEBUG(1, ("Could not lock \"%s\"\n", keystr));
148			return False;
149		}
150
151		snprintf(sessionid.id_str, sizeof(sessionid.id_str),
152			 SESSION_TEMPLATE, (long unsigned int)sys_getpid(),
153			 vuser->vuid);
154	}
155
156	SMB_ASSERT(rec != NULL);
157
158	/* If 'hostname lookup' == yes, then do the DNS lookup.  This is
159           needed because utmp and PAM both expect DNS names
160
161	   client_name() handles this case internally.
162	*/
163
164	hostname = client_name(get_client_fd());
165	if (strcmp(hostname, "UNKNOWN") == 0) {
166		hostname = client_addr(get_client_fd(),addr,sizeof(addr));
167	}
168
169	fstrcpy(sessionid.username, vuser->server_info->unix_name);
170	fstrcpy(sessionid.hostname, hostname);
171	sessionid.id_num = i;  /* Only valid for utmp sessions */
172	sessionid.pid = pid;
173	sessionid.uid = vuser->server_info->utok.uid;
174	sessionid.gid = vuser->server_info->utok.gid;
175	fstrcpy(sessionid.remote_machine, get_remote_machine_name());
176	fstrcpy(sessionid.ip_addr_str,
177		client_addr(get_client_fd(),addr,sizeof(addr)));
178	sessionid.connect_start = time(NULL);
179
180	if (!smb_pam_claim_session(sessionid.username, sessionid.id_str,
181				   sessionid.hostname)) {
182		DEBUG(1,("pam_session rejected the session for %s [%s]\n",
183				sessionid.username, sessionid.id_str));
184
185		TALLOC_FREE(rec);
186		return False;
187	}
188
189	data.dptr = (uint8 *)&sessionid;
190	data.dsize = sizeof(sessionid);
191
192	status = rec->store(rec, data, TDB_REPLACE);
193
194	TALLOC_FREE(rec);
195
196	if (!NT_STATUS_IS_OK(status)) {
197		DEBUG(1,("session_claim: unable to create session id "
198			 "record: %s\n", nt_errstr(status)));
199		return False;
200	}
201
202	if (lp_utmp()) {
203		sys_utmp_claim(sessionid.username, sessionid.hostname,
204			       sessionid.ip_addr_str,
205			       sessionid.id_str, sessionid.id_num);
206	}
207
208	vuser->session_keystr = talloc_strdup(vuser, keystr);
209	if (!vuser->session_keystr) {
210		DEBUG(0, ("session_claim:  talloc_strdup() failed for session_keystr\n"));
211		return False;
212	}
213	return True;
214}
215
216/********************************************************************
217 called when a session is destroyed
218********************************************************************/
219
220void session_yield(user_struct *vuser)
221{
222	TDB_DATA key;
223	struct sessionid sessionid;
224	struct db_context *ctx;
225	struct db_record *rec;
226
227	if (!(ctx = session_db_ctx())) return;
228
229	if (!vuser->session_keystr) {
230		return;
231	}
232
233	key = string_term_tdb_data(vuser->session_keystr);
234
235	if (!(rec = ctx->fetch_locked(ctx, NULL, key))) {
236		return;
237	}
238
239	if (rec->value.dsize != sizeof(sessionid))
240		return;
241
242	memcpy(&sessionid, rec->value.dptr, sizeof(sessionid));
243
244	if (lp_utmp()) {
245		sys_utmp_yield(sessionid.username, sessionid.hostname,
246			       sessionid.ip_addr_str,
247			       sessionid.id_str, sessionid.id_num);
248	}
249
250	smb_pam_close_session(sessionid.username, sessionid.id_str,
251			      sessionid.hostname);
252
253	rec->delete_rec(rec);
254
255	TALLOC_FREE(rec);
256}
257
258/********************************************************************
259********************************************************************/
260
261static bool session_traverse(int (*fn)(struct db_record *db,
262				       void *private_data),
263			     void *private_data)
264{
265	struct db_context *ctx;
266
267	if (!(ctx = session_db_ctx())) {
268		DEBUG(3, ("No tdb opened\n"));
269		return False;
270	}
271
272	ctx->traverse_read(ctx, fn, private_data);
273	return True;
274}
275
276/********************************************************************
277********************************************************************/
278
279struct session_list {
280	TALLOC_CTX *mem_ctx;
281	int count;
282	struct sessionid *sessions;
283};
284
285static int gather_sessioninfo(struct db_record *rec, void *state)
286{
287	struct session_list *sesslist = (struct session_list *) state;
288	const struct sessionid *current =
289		(const struct sessionid *) rec->value.dptr;
290
291	sesslist->sessions = TALLOC_REALLOC_ARRAY(
292		sesslist->mem_ctx, sesslist->sessions, struct sessionid,
293		sesslist->count+1);
294
295	if (!sesslist->sessions) {
296		sesslist->count = 0;
297		return -1;
298	}
299
300	memcpy(&sesslist->sessions[sesslist->count], current,
301	       sizeof(struct sessionid));
302
303	sesslist->count++;
304
305	DEBUG(7,("gather_sessioninfo session from %s@%s\n",
306		 current->username, current->remote_machine));
307
308	return 0;
309}
310
311/********************************************************************
312********************************************************************/
313
314int list_sessions(TALLOC_CTX *mem_ctx, struct sessionid **session_list)
315{
316	struct session_list sesslist;
317
318	sesslist.mem_ctx = mem_ctx;
319	sesslist.count = 0;
320	sesslist.sessions = NULL;
321
322	if (!session_traverse(gather_sessioninfo, (void *) &sesslist)) {
323		DEBUG(3, ("Session traverse failed\n"));
324		SAFE_FREE(sesslist.sessions);
325		*session_list = NULL;
326		return 0;
327	}
328
329	*session_list = sesslist.sessions;
330	return sesslist.count;
331}
332