ncpl_conn.c revision 84768
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 84768 2001-10-10 17:48:44Z bde $");
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/sysctl.h>
51#include <sys/ioctl.h>
52#include <sys/time.h>
53#include <sys/mount.h>
54#include <fcntl.h>
55#include <ctype.h>
56#include <errno.h>
57#include <stdio.h>
58#include <string.h>
59#include <stdlib.h>
60#include <pwd.h>
61#include <grp.h>
62#include <unistd.h>
63
64#include <netncp/ncp_lib.h>
65#include <netncp/ncp_rcfile.h>
66#include <fs/nwfs/nwfs.h>
67
68static char *server_name; /* need a better way ! */
69
70
71
72int
73ncp_li_setserver(struct ncp_conn_loginfo *li, const char *arg) {
74	if (strlen(arg) >= NCP_BINDERY_NAME_LEN) {
75		ncp_error("server name '%s' too long", 0, arg);
76		return ENAMETOOLONG;
77	}
78	ncp_str_upper(strcpy(li->server, arg));
79	return 0;
80}
81
82int
83ncp_li_setuser(struct ncp_conn_loginfo *li, char *arg) {
84	if (arg && strlen(arg) >= NCP_BINDERY_NAME_LEN) {
85		ncp_error("user name '%s' too long", 0, arg);
86		return ENAMETOOLONG;
87	}
88	if (li->user)
89		free(li->user);
90	if (arg) {
91		li->user = strdup(arg);
92		if (li->user == NULL)
93			return ENOMEM;
94		ncp_str_upper(li->user);
95	} else
96		li->user = NULL;
97	return 0;
98}
99
100int
101ncp_li_setpassword(struct ncp_conn_loginfo *li, const char *passwd) {
102	if (passwd && strlen(passwd) >= 127) {
103		ncp_error("password too long", 0);
104		return ENAMETOOLONG;
105	}
106	if (li->password) {
107		bzero(li->password, strlen(li->password));
108		free(li->password);
109	}
110	if (passwd) {
111		li->password = strdup(passwd);
112		if (li->password == NULL)
113			return ENOMEM;
114	} else
115		li->password = NULL;
116	return 0;
117}
118/*
119 * Prescan command line for [-S server] [-U user] arguments
120 * and fill li structure with defaults
121 */
122int
123ncp_li_init(struct ncp_conn_loginfo *li, int argc, char *argv[]) {
124	int  opt, error = 0;
125	char *arg;
126
127	bzero(li,sizeof(*li));
128	li->timeout = 15;	/* these values should be large enough to handle */
129	li->retry_count = 4;	/* slow servers, even on ethernet */
130	li->access_mode = 0;
131	li->password = NULL;
132	li->sig_level = 1;
133	li->objtype = NCP_BINDERY_USER;
134	li->owner = NCP_DEFAULT_OWNER;
135	li->group = NCP_DEFAULT_GROUP;
136	server_name = NULL;
137	if (argv == NULL) return 0;
138	while (error == 0 && (opt = ncp_getopt(argc, argv, ":S:U:")) != -1) {
139		arg = ncp_optarg;
140		switch (opt) {
141		    case 'S':
142			error = ncp_li_setserver(li, arg);
143			break;
144		    case 'U':
145			error = ncp_li_setuser(li, arg);
146			break;
147		}
148	}
149	ncp_optind = ncp_optreset = 1;
150	return error;
151}
152
153void
154ncp_li_done(struct ncp_conn_loginfo *li) {
155	if (li->user)
156		free(li->user);
157	if (li->password)
158		free(li->password);
159}
160
161/*
162 * Lookup existing connection based on li structure, if connection
163 * found, it will be referenced. Otherwise full login sequence performed.
164 */
165int
166ncp_li_login(struct ncp_conn_loginfo *li, int *aconnid) {
167	int connHandle, error;
168
169	if ((error = ncp_conn_scan(li, &connHandle)) == 0) {
170		*aconnid = connHandle;
171		return 0;
172	}
173	error = ncp_connect(li, &connHandle);
174	if (error) return errno;
175	error = ncp_login(connHandle, li->user, li->objtype, li->password);
176	if (error) {
177		ncp_disconnect(connHandle);
178	} else
179		*aconnid = connHandle;
180	return error;
181}
182
183/*
184 * read rc file as follows:
185 * 1. read [server] section
186 * 2. override with [server:user] section
187 * Since abcence of rcfile is not a bug, silently ignore that fact.
188 * rcfile never closed to reduce number of open/close operations.
189 */
190int
191ncp_li_readrc(struct ncp_conn_loginfo *li) {
192	int i, val, error;
193	char uname[NCP_BINDERY_NAME_LEN*2+1];
194	char *sect = NULL, *p;
195
196	/*
197	 * if info from cmd line incomplete, try to find existing
198	 * connection and fill server/user from it.
199	 */
200	if (li->server[0] == 0 || li->user == NULL) {
201		int connHandle;
202		struct ncp_conn_stat cs;
203
204		if ((error = ncp_conn_scan(li, &connHandle)) != 0) {
205			ncp_error("no default connection found", errno);
206			return error;
207		}
208		ncp_conn_getinfo(connHandle, &cs);
209		ncp_li_setserver(li, cs.li.server);
210		ncp_li_setuser(li, cs.user);
211		ncp_li_setpassword(li, "");
212		ncp_disconnect(connHandle);
213	}
214	if (ncp_open_rcfile()) 	return 0;
215
216	for (i = 0; i < 2; i++) {
217		switch (i) {
218		    case 0:
219			sect = li->server;
220			break;
221		    case 1:
222			strcat(strcat(strcpy(uname,li->server),":"),li->user ? li->user : "default");
223			sect = uname;
224			break;
225		}
226		rc_getstringptr(ncp_rc, sect, "password", &p);
227		if (p)
228			ncp_li_setpassword(li, p);
229		rc_getint(ncp_rc,sect, "timeout", &li->timeout);
230		rc_getint(ncp_rc,sect, "retry_count", &li->retry_count);
231		rc_getint(ncp_rc,sect, "sig_level", &li->sig_level);
232		if (rc_getint(ncp_rc,sect,"access_mode",&val) == 0)
233			li->access_mode = val;
234		if(rc_getbool(ncp_rc,sect,"bindery",&val) == 0 && val) {
235			li->opt |= NCP_OPT_BIND;
236		}
237	}
238	return 0;
239}
240
241/*
242 * check for all uncompleted fields
243 */
244int
245ncp_li_check(struct ncp_conn_loginfo *li) {
246	int error = 0;
247	char *p;
248
249	do {
250		if (li->server[0] == 0) {
251			ncp_error("no server name specified", 0);
252			error = 1;
253			break;
254		}
255		error = ncp_find_fileserver(li,
256		    (server_name==NULL) ? AF_IPX : AF_INET, server_name);
257		if (error) {
258			ncp_error("can't find server %s", error, li->server);
259			break;
260		}
261		if (li->user == NULL || li->user[0] == 0) {
262			ncp_error("no user name specified for server %s",
263			    0, li->server);
264			error = 1;
265			break;
266		}
267		if (li->password == NULL) {
268			p = getpass("Netware password:");
269			error = ncp_li_setpassword(li, p) ? 1 : 0;
270		}
271	} while (0);
272	return error;
273}
274
275int
276ncp_conn_cnt(void) {
277	int error, cnt = 0, len = sizeof(cnt);
278
279#if __FreeBSD_version < 400001
280	error = sysctlbyname("net.ipx.ncp.conn_cnt", &cnt, &len, NULL, 0);
281#else
282	error = sysctlbyname("net.ncp.conn_cnt", &cnt, &len, NULL, 0);
283#endif
284	if (error) cnt = 0;
285	return cnt;
286}
287
288/*
289 * Find an existing connection and reference it
290 */
291int
292ncp_conn_find(char *server,char *user) {
293	struct ncp_conn_args ca;
294	int connid, error;
295
296	if (server == NULL && user == NULL) {
297		error = ncp_conn_scan(NULL,&connid);
298		if (error) return -2;
299		return connid;
300	}
301	if (server == NULL)
302		return -2;
303	ncp_str_upper(server);
304	if (user) ncp_str_upper(user);
305	bzero(&ca, sizeof(ca));
306	ncp_li_setserver(&ca, server);
307	ncp_li_setuser(&ca, user);
308	error = ncp_conn_scan(&ca,&connid);
309	if (error)
310		connid = -1;
311	return connid;
312}
313
314int
315ncp_li_arg(struct ncp_conn_loginfo *li, int opt, char *arg) {
316	int error = 0, sig_level;
317	char *p, *cp;
318	struct group *gr;
319	struct passwd *pw;
320
321	switch(opt) {
322	    case 'S': /* we already fill server/[user] pair */
323	    case 'U':
324		break;
325	    case 'A':
326		server_name = arg;
327		break;
328	    case 'B':
329		li->opt |= NCP_OPT_BIND;
330		break;
331	    case 'C':
332		li->opt |= NCP_OPT_NOUPCASEPASS;
333		break;
334	    case 'I':
335		sig_level = atoi(arg);
336		if (sig_level < 0 || sig_level > 3) {
337			ncp_error("invalid NCP signature level option `%s'\
338			    (must be a number between 0 and 3)", 0, arg);
339			error = 1;
340		}
341		li->sig_level = sig_level;
342		if (sig_level > 1) li->opt |= NCP_OPT_SIGN;
343		break;
344	    case 'M':
345		li->access_mode = strtol(arg, NULL, 8);
346		break;
347	    case 'N':
348		ncp_li_setpassword(li, "");
349		break;
350	    case 'O':
351		p = strdup(arg);
352		cp = strchr(p, ':');
353		if (cp) {
354			*cp++ = '\0';
355			if (*cp) {
356				gr = getgrnam(cp);
357				if (gr) {
358					li->group = gr->gr_gid;
359				} else
360					ncp_error("Invalid group name %s, ignored",
361					    0, cp);
362			}
363		}
364		if (*p) {
365			pw = getpwnam(p);
366			if (pw) {
367				li->owner = pw->pw_uid;
368			} else
369				ncp_error("Invalid user name %s, ignored", 0, p);
370		}
371		endpwent();
372		free(p);
373		break;
374	    case 'P':
375		li->opt |= NCP_OPT_PERMANENT;
376		break;
377	    case 'R':
378		li->retry_count = atoi(arg);
379		break;
380	    case 'W':
381		li->timeout = atoi(arg);
382		break;
383	}
384	return error;
385}
386
387void *
388ncp_conn_list(void) {
389	int error, cnt = 0, len;
390	void *p;
391
392	cnt = ncp_conn_cnt();
393	if (cnt == 0) return NULL;
394	len = cnt*(sizeof(struct ncp_conn_stat))+sizeof(int);
395	p = malloc(len);
396	if (p == NULL) return NULL;
397#if __FreeBSD_version < 400001
398	error = sysctlbyname("net.ipx.ncp.conn_stat", p, &len, NULL, 0);
399#else
400	error = sysctlbyname("net.ncp.conn_stat", p, &len, NULL, 0);
401#endif
402	if (error) {
403		free(p);
404		p = NULL;
405	}
406	return p;
407}
408
409
410int
411ncp_conn_setflags(int connid, u_int16_t mask, u_int16_t flags) {
412	int error;
413	DECLARE_RQ;
414
415	ncp_init_request(conn);
416	ncp_add_byte(conn, NCP_CONN_SETFLAGS);
417	ncp_add_word_lh(conn, mask);
418	ncp_add_word_lh(conn, flags);
419	if ((error = ncp_conn_request(connid, conn)) < 0)
420		return -1;
421	return error;
422}
423
424int
425ncp_login(int connHandle, const char *user, int objtype, const char *password) {
426	int error;
427	struct ncp_conn_login *p;
428	DECLARE_RQ;
429
430	ncp_init_request(conn);
431	ncp_add_byte(conn, NCP_CONN_LOGIN);
432	p = (struct ncp_conn_login *)&conn->packet[conn->rqsize];
433	(const char*)p->username = user;
434	p->objtype = objtype;
435	(const char*)p->password = password;
436	conn->rqsize += sizeof(*p);
437	if ((error = ncp_conn_request(connHandle, conn)) < 0)
438		return -1;
439	return error;
440}
441
442int
443ncp_connect_addr(struct sockaddr *sa, NWCONN_HANDLE *chp) {
444	int error;
445	struct ncp_conn_args li;
446
447	bzero(&li, sizeof(li));
448	bcopy(sa, &li.addr, sa->sa_len);
449	/*
450	 * XXX Temporary !!!. server will be filled in kernel !!!
451	 */
452	strcpy(li.server,ipx_ntoa(li.ipxaddr.sipx_addr));
453	error = ncp_connect(&li, chp);
454	return error;
455}
456
457int
458ncp_conn_getinfo(int connHandle, struct ncp_conn_stat *ps) {
459	int error;
460	DECLARE_RQ;
461
462	ncp_init_request(conn);
463	ncp_add_byte(conn, NCP_CONN_GETINFO);
464	if ((error = ncp_conn_request(connHandle, conn)) < 0)
465		return -1;
466	memcpy(ps, ncp_reply_data(conn,0), sizeof(*ps));
467	return error;
468}
469
470int
471ncp_conn_getuser(int connHandle, char **user) {
472	int error;
473	DECLARE_RQ;
474
475	ncp_init_request(conn);
476	ncp_add_byte(conn, NCP_CONN_GETUSER);
477	if ((error = ncp_conn_request(connHandle, conn)) < 0)
478		return -1;
479	*user = strdup(ncp_reply_data(conn,0));
480	return error;
481}
482
483int
484ncp_conn2ref(int connHandle, int *connRef) {
485	int error;
486	DECLARE_RQ;
487
488	ncp_init_request(conn);
489	ncp_add_byte(conn, NCP_CONN_CONN2REF);
490	if ((error = ncp_conn_request(connHandle, conn)) < 0)
491		return -1;
492	*connRef = *((int*)ncp_reply_data(conn,0));
493	return error;
494}
495
496int
497ncp_path2conn(char *path, int *connHandle) {
498	struct statfs st;
499	int d, error;
500
501	if ((error = statfs(path, &st)) != 0) return errno;
502	if (strcmp(st.f_fstypename,"nwfs") != 0) return EINVAL;
503	if ((d = open(path, O_RDONLY)) < 0) return errno;
504	if ((error = ioctl(d,NWFSIOC_GETCONN, connHandle)) != 0) return errno;
505	close(d);
506	return 0;
507}
508
509int
510ncp_conn_dup(NWCONN_HANDLE org, NWCONN_HANDLE *res) {
511	int error;
512	DECLARE_RQ;
513
514	ncp_init_request(conn);
515	ncp_add_byte(conn, NCP_CONN_DUP);
516	if ((error = ncp_conn_request(org, conn)) < 0)
517		return errno;
518	*res = *((int*)ncp_reply_data(conn, 0));
519	return 0;
520}
521