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