ncpl_conn.c revision 84213
1/*
2 * Copyright (c) 1999, Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *    This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: head/lib/libncp/ncpl_conn.c 84213 2001-09-30 22:01:19Z dillon $");
35
36/*
37 *
38 * Current scheme to create/open connection:
39 * 1. ncp_li_init() - lookup -S [-U] options in command line
40 * 2. ncp_li_init() - try to find existing connection
41 * 3. ncp_li_init() - if no server name and no accessible connections - bail out
42 * 4. This is connection candidate, read .rc file, override with command line
43 *    and go ahead
44 * Note: connection referenced only via ncp_login() call. Although it is
45 * possible to get connection handle in other way, it will be unwise to use
46 * it, since conn can be destroyed at any time.
47 *
48 */
49#include <sys/param.h>
50#include <sys/lock.h>
51#include <sys/mutex.h>
52#include <sys/sysctl.h>
53#include <sys/ioctl.h>
54#include <sys/time.h>
55#include <sys/mount.h>
56#include <fcntl.h>
57#include <ctype.h>
58#include <errno.h>
59#include <stdio.h>
60#include <string.h>
61#include <stdlib.h>
62#include <pwd.h>
63#include <grp.h>
64#include <unistd.h>
65
66#include <netncp/ncp_lib.h>
67#include <netncp/ncp_rcfile.h>
68#include <fs/nwfs/nwfs.h>
69
70static char *server_name; /* need a better way ! */
71
72
73
74int
75ncp_li_setserver(struct ncp_conn_loginfo *li, const char *arg) {
76	if (strlen(arg) >= NCP_BINDERY_NAME_LEN) {
77		ncp_error("server name '%s' too long", 0, arg);
78		return ENAMETOOLONG;
79	}
80	ncp_str_upper(strcpy(li->server, arg));
81	return 0;
82}
83
84int
85ncp_li_setuser(struct ncp_conn_loginfo *li, char *arg) {
86	if (arg && strlen(arg) >= NCP_BINDERY_NAME_LEN) {
87		ncp_error("user name '%s' too long", 0, arg);
88		return ENAMETOOLONG;
89	}
90	if (li->user)
91		free(li->user);
92	if (arg) {
93		li->user = strdup(arg);
94		if (li->user == NULL)
95			return ENOMEM;
96		ncp_str_upper(li->user);
97	} else
98		li->user = NULL;
99	return 0;
100}
101
102int
103ncp_li_setpassword(struct ncp_conn_loginfo *li, const char *passwd) {
104	if (passwd && strlen(passwd) >= 127) {
105		ncp_error("password too long", 0);
106		return ENAMETOOLONG;
107	}
108	if (li->password) {
109		bzero(li->password, strlen(li->password));
110		free(li->password);
111	}
112	if (passwd) {
113		li->password = strdup(passwd);
114		if (li->password == NULL)
115			return ENOMEM;
116	} else
117		li->password = NULL;
118	return 0;
119}
120/*
121 * Prescan command line for [-S server] [-U user] arguments
122 * and fill li structure with defaults
123 */
124int
125ncp_li_init(struct ncp_conn_loginfo *li, int argc, char *argv[]) {
126	int  opt, error = 0;
127	char *arg;
128
129	bzero(li,sizeof(*li));
130	li->timeout = 15;	/* these values should be large enough to handle */
131	li->retry_count = 4;	/* slow servers, even on ethernet */
132	li->access_mode = 0;
133	li->password = NULL;
134	li->sig_level = 1;
135	li->objtype = NCP_BINDERY_USER;
136	li->owner = NCP_DEFAULT_OWNER;
137	li->group = NCP_DEFAULT_GROUP;
138	server_name = NULL;
139	if (argv == NULL) return 0;
140	while (error == 0 && (opt = ncp_getopt(argc, argv, ":S:U:")) != -1) {
141		arg = ncp_optarg;
142		switch (opt) {
143		    case 'S':
144			error = ncp_li_setserver(li, arg);
145			break;
146		    case 'U':
147			error = ncp_li_setuser(li, arg);
148			break;
149		}
150	}
151	ncp_optind = ncp_optreset = 1;
152	return error;
153}
154
155void
156ncp_li_done(struct ncp_conn_loginfo *li) {
157	if (li->user)
158		free(li->user);
159	if (li->password)
160		free(li->password);
161}
162
163/*
164 * Lookup existing connection based on li structure, if connection
165 * found, it will be referenced. Otherwise full login sequence performed.
166 */
167int
168ncp_li_login(struct ncp_conn_loginfo *li, int *aconnid) {
169	int connHandle, error;
170
171	if ((error = ncp_conn_scan(li, &connHandle)) == 0) {
172		*aconnid = connHandle;
173		return 0;
174	}
175	error = ncp_connect(li, &connHandle);
176	if (error) return errno;
177	error = ncp_login(connHandle, li->user, li->objtype, li->password);
178	if (error) {
179		ncp_disconnect(connHandle);
180	} else
181		*aconnid = connHandle;
182	return error;
183}
184
185/*
186 * read rc file as follows:
187 * 1. read [server] section
188 * 2. override with [server:user] section
189 * Since abcence of rcfile is not a bug, silently ignore that fact.
190 * rcfile never closed to reduce number of open/close operations.
191 */
192int
193ncp_li_readrc(struct ncp_conn_loginfo *li) {
194	int i, val, error;
195	char uname[NCP_BINDERY_NAME_LEN*2+1];
196	char *sect = NULL, *p;
197
198	/*
199	 * if info from cmd line incomplete, try to find existing
200	 * connection and fill server/user from it.
201	 */
202	if (li->server[0] == 0 || li->user == NULL) {
203		int connHandle;
204		struct ncp_conn_stat cs;
205
206		if ((error = ncp_conn_scan(li, &connHandle)) != 0) {
207			ncp_error("no default connection found", errno);
208			return error;
209		}
210		ncp_conn_getinfo(connHandle, &cs);
211		ncp_li_setserver(li, cs.li.server);
212		ncp_li_setuser(li, cs.user);
213		ncp_li_setpassword(li, "");
214		ncp_disconnect(connHandle);
215	}
216	if (ncp_open_rcfile()) 	return 0;
217
218	for (i = 0; i < 2; i++) {
219		switch (i) {
220		    case 0:
221			sect = li->server;
222			break;
223		    case 1:
224			strcat(strcat(strcpy(uname,li->server),":"),li->user ? li->user : "default");
225			sect = uname;
226			break;
227		}
228		rc_getstringptr(ncp_rc, sect, "password", &p);
229		if (p)
230			ncp_li_setpassword(li, p);
231		rc_getint(ncp_rc,sect, "timeout", &li->timeout);
232		rc_getint(ncp_rc,sect, "retry_count", &li->retry_count);
233		rc_getint(ncp_rc,sect, "sig_level", &li->sig_level);
234		if (rc_getint(ncp_rc,sect,"access_mode",&val) == 0)
235			li->access_mode = val;
236		if(rc_getbool(ncp_rc,sect,"bindery",&val) == 0 && val) {
237			li->opt |= NCP_OPT_BIND;
238		}
239	}
240	return 0;
241}
242
243/*
244 * check for all uncompleted fields
245 */
246int
247ncp_li_check(struct ncp_conn_loginfo *li) {
248	int error = 0;
249	char *p;
250
251	do {
252		if (li->server[0] == 0) {
253			ncp_error("no server name specified", 0);
254			error = 1;
255			break;
256		}
257		error = ncp_find_fileserver(li,
258		    (server_name==NULL) ? AF_IPX : AF_INET, server_name);
259		if (error) {
260			ncp_error("can't find server %s", error, li->server);
261			break;
262		}
263		if (li->user == NULL || li->user[0] == 0) {
264			ncp_error("no user name specified for server %s",
265			    0, li->server);
266			error = 1;
267			break;
268		}
269		if (li->password == NULL) {
270			p = getpass("Netware password:");
271			error = ncp_li_setpassword(li, p) ? 1 : 0;
272		}
273	} while (0);
274	return error;
275}
276
277int
278ncp_conn_cnt(void) {
279	int error, cnt = 0, len = sizeof(cnt);
280
281#if __FreeBSD_version < 400001
282	error = sysctlbyname("net.ipx.ncp.conn_cnt", &cnt, &len, NULL, 0);
283#else
284	error = sysctlbyname("net.ncp.conn_cnt", &cnt, &len, NULL, 0);
285#endif
286	if (error) cnt = 0;
287	return cnt;
288}
289
290/*
291 * Find an existing connection and reference it
292 */
293int
294ncp_conn_find(char *server,char *user) {
295	struct ncp_conn_args ca;
296	int connid, error;
297
298	if (server == NULL && user == NULL) {
299		error = ncp_conn_scan(NULL,&connid);
300		if (error) return -2;
301		return connid;
302	}
303	if (server == NULL)
304		return -2;
305	ncp_str_upper(server);
306	if (user) ncp_str_upper(user);
307	bzero(&ca, sizeof(ca));
308	ncp_li_setserver(&ca, server);
309	ncp_li_setuser(&ca, user);
310	error = ncp_conn_scan(&ca,&connid);
311	if (error)
312		connid = -1;
313	return connid;
314}
315
316int
317ncp_li_arg(struct ncp_conn_loginfo *li, int opt, char *arg) {
318	int error = 0, sig_level;
319	char *p, *cp;
320	struct group *gr;
321	struct passwd *pw;
322
323	switch(opt) {
324	    case 'S': /* we already fill server/[user] pair */
325	    case 'U':
326		break;
327	    case 'A':
328		server_name = arg;
329		break;
330	    case 'B':
331		li->opt |= NCP_OPT_BIND;
332		break;
333	    case 'C':
334		li->opt |= NCP_OPT_NOUPCASEPASS;
335		break;
336	    case 'I':
337		sig_level = atoi(arg);
338		if (sig_level < 0 || sig_level > 3) {
339			ncp_error("invalid NCP signature level option `%s'\
340			    (must be a number between 0 and 3)", 0, arg);
341			error = 1;
342		}
343		li->sig_level = sig_level;
344		if (sig_level > 1) li->opt |= NCP_OPT_SIGN;
345		break;
346	    case 'M':
347		li->access_mode = strtol(arg, NULL, 8);
348		break;
349	    case 'N':
350		ncp_li_setpassword(li, "");
351		break;
352	    case 'O':
353		p = strdup(arg);
354		cp = strchr(p, ':');
355		if (cp) {
356			*cp++ = '\0';
357			if (*cp) {
358				gr = getgrnam(cp);
359				if (gr) {
360					li->group = gr->gr_gid;
361				} else
362					ncp_error("Invalid group name %s, ignored",
363					    0, cp);
364			}
365		}
366		if (*p) {
367			pw = getpwnam(p);
368			if (pw) {
369				li->owner = pw->pw_uid;
370			} else
371				ncp_error("Invalid user name %s, ignored", 0, p);
372		}
373		endpwent();
374		free(p);
375		break;
376	    case 'P':
377		li->opt |= NCP_OPT_PERMANENT;
378		break;
379	    case 'R':
380		li->retry_count = atoi(arg);
381		break;
382	    case 'W':
383		li->timeout = atoi(arg);
384		break;
385	}
386	return error;
387}
388
389void *
390ncp_conn_list(void) {
391	int error, cnt = 0, len;
392	void *p;
393
394	cnt = ncp_conn_cnt();
395	if (cnt == 0) return NULL;
396	len = cnt*(sizeof(struct ncp_conn_stat))+sizeof(int);
397	p = malloc(len);
398	if (p == NULL) return NULL;
399#if __FreeBSD_version < 400001
400	error = sysctlbyname("net.ipx.ncp.conn_stat", p, &len, NULL, 0);
401#else
402	error = sysctlbyname("net.ncp.conn_stat", p, &len, NULL, 0);
403#endif
404	if (error) {
405		free(p);
406		p = NULL;
407	}
408	return p;
409}
410
411
412int
413ncp_conn_setflags(int connid, u_int16_t mask, u_int16_t flags) {
414	int error;
415	DECLARE_RQ;
416
417	ncp_init_request(conn);
418	ncp_add_byte(conn, NCP_CONN_SETFLAGS);
419	ncp_add_word_lh(conn, mask);
420	ncp_add_word_lh(conn, flags);
421	if ((error = ncp_conn_request(connid, conn)) < 0)
422		return -1;
423	return error;
424}
425
426int
427ncp_login(int connHandle, const char *user, int objtype, const char *password) {
428	int error;
429	struct ncp_conn_login *p;
430	DECLARE_RQ;
431
432	ncp_init_request(conn);
433	ncp_add_byte(conn, NCP_CONN_LOGIN);
434	p = (struct ncp_conn_login *)&conn->packet[conn->rqsize];
435	(const char*)p->username = user;
436	p->objtype = objtype;
437	(const char*)p->password = password;
438	conn->rqsize += sizeof(*p);
439	if ((error = ncp_conn_request(connHandle, conn)) < 0)
440		return -1;
441	return error;
442}
443
444int
445ncp_connect_addr(struct sockaddr *sa, NWCONN_HANDLE *chp) {
446	int error;
447	struct ncp_conn_args li;
448
449	bzero(&li, sizeof(li));
450	bcopy(sa, &li.addr, sa->sa_len);
451	/*
452	 * XXX Temporary !!!. server will be filled in kernel !!!
453	 */
454	strcpy(li.server,ipx_ntoa(li.ipxaddr.sipx_addr));
455	error = ncp_connect(&li, chp);
456	return error;
457}
458
459int
460ncp_conn_getinfo(int connHandle, struct ncp_conn_stat *ps) {
461	int error;
462	DECLARE_RQ;
463
464	ncp_init_request(conn);
465	ncp_add_byte(conn, NCP_CONN_GETINFO);
466	if ((error = ncp_conn_request(connHandle, conn)) < 0)
467		return -1;
468	memcpy(ps, ncp_reply_data(conn,0), sizeof(*ps));
469	return error;
470}
471
472int
473ncp_conn_getuser(int connHandle, char **user) {
474	int error;
475	DECLARE_RQ;
476
477	ncp_init_request(conn);
478	ncp_add_byte(conn, NCP_CONN_GETUSER);
479	if ((error = ncp_conn_request(connHandle, conn)) < 0)
480		return -1;
481	*user = strdup(ncp_reply_data(conn,0));
482	return error;
483}
484
485int
486ncp_conn2ref(int connHandle, int *connRef) {
487	int error;
488	DECLARE_RQ;
489
490	ncp_init_request(conn);
491	ncp_add_byte(conn, NCP_CONN_CONN2REF);
492	if ((error = ncp_conn_request(connHandle, conn)) < 0)
493		return -1;
494	*connRef = *((int*)ncp_reply_data(conn,0));
495	return error;
496}
497
498int
499ncp_path2conn(char *path, int *connHandle) {
500	struct statfs st;
501	int d, error;
502
503	if ((error = statfs(path, &st)) != 0) return errno;
504	if (strcmp(st.f_fstypename,"nwfs") != 0) return EINVAL;
505	if ((d = open(path, O_RDONLY)) < 0) return errno;
506	if ((error = ioctl(d,NWFSIOC_GETCONN, connHandle)) != 0) return errno;
507	close(d);
508	return 0;
509}
510
511int
512ncp_conn_dup(NWCONN_HANDLE org, NWCONN_HANDLE *res) {
513	int error;
514	DECLARE_RQ;
515
516	ncp_init_request(conn);
517	ncp_add_byte(conn, NCP_CONN_DUP);
518	if ((error = ncp_conn_request(org, conn)) < 0)
519		return errno;
520	*res = *((int*)ncp_reply_data(conn, 0));
521	return 0;
522}
523