4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
31 #pragma ident "%Z%%M% %I% %E% SMI"
33 /* system include files */
41 #include <sys/utsname.h>
42 #include <sys/tiuser.h>
43 #include <sys/param.h>
44 #include <sys/types.h>
46 #include <sys/mkdev.h>
52 #include <sys/stropts.h>
57 /* listener include files */
59 #include "lsparam.h" /* listener parameters */
60 #include "lsfiles.h" /* listener files info */
61 #include "lserror.h" /* listener error codes */
62 #include "lsnlsmsg.h" /* NLPS listener protocol */
63 #include "lssmbmsg.h" /* MS_NET identifier */
64 #include "lsdbf.h" /* data base file stuff */
67 /* global variables */
69 FILE *Logfp
; /* file pointer for nlps_server's log file */
71 FILE *Debugfp
; /* debugging output */
74 int Dbf_entries
; /* number of private addresses in dbf file */
78 char *Server_cmd_lines
;
83 * These global symbols are used for logging.
84 * Pid, NLPS_proc, and Lastmsg are significant here; the others aren't used.
95 char Altbasedir
[BUFSIZ
];
97 extern char *getenv();
99 static void nls_reply(int code
, char *text
);
100 static void nullfix(void);
103 main(int argc
, char **argv
)
105 extern int read_dbf();
108 provider
= getenv("PMTAG");
109 sprintf(Altbasedir
, "%s/%s/", ALTDIR
, provider
);
110 sprintf(Basedir
, "%s/%s/", BASEDIR
, provider
);
111 sprintf(msgbuf
, "%s/%s", Altbasedir
, LOGNAME
);
112 if (!(Logfp
= fopen(msgbuf
, "a+"))) {
117 sprintf(msgbuf
, "%s/%s", Altbasedir
, PDEBUGNAME
);
118 if (!(Debugfp
= fopen(msgbuf
, "a"))) {
119 logmessage("NLPS: Unable to open DEBUG file");
125 * re-sync TLI structures after we were exec'ed from listener
128 if (t_sync(0) == -1) {
129 DEBUG((9,"t_sync failed, t_errno %d", t_errno
));
130 logmessage("NLPS: Resynchronization of TLI failed");
150 dbf_t
*getdbfentry();
151 extern char **mkdbfargv();
154 DEBUG((9,"in nlps_server (NLPS/SMB message), pid %ld", Pid
));
156 if ((size
= getrequest(bp
)) <= 0) {
157 logmessage("NLPS: No/bad service request received");
162 DEBUG((7,"nlps_server(): Error returned from getrequest()" ));
163 logmessage("NLPS: Error returned from getrequest()");
168 * if message is NLPS protocol...
171 if ((!strncmp(bp
,NLPSIDSTR
,NLPSIDSZ
)) && /* NLPS request */
172 (*(bp
+ NLPSIDSZ
) == NLPSSEPCHAR
)) {
173 nls_service(bp
, size
);
174 (void)sleep(10); /* if returned to here, then
175 * must sleep for a short period of time to
176 * insure that the client received any possible
177 * exit response message from the listener.
181 * else if message is for the MS-NET file server...
184 } else if ( (*bp
== (char)0xff) && (!strncmp(bp
+1,SMBIDSTR
,SMBIDSZ
)) ) {
185 if (dbp
= getdbfentry(DBF_SMB_CODE
))
186 if (dbp
->dbf_flags
& DBF_OFF
)
187 logmessage("NLPS: SMB message, server disabled in data base");
189 argv
= mkdbfargv(dbp
);
190 smbservice(bp
, size
, argv
);
193 logmessage("NLPS: SMB message, no data base entry");
196 * else, message type is unknown...
200 logmessage("NLPS: Unknown service request (ignored)");
201 DEBUG((7,"msg size: %d; 1st four chars (hex) %x %x %x %x",
202 *bp
, *(bp
+1), *(bp
+2), *(bp
+3)));
206 * the routines that start servers return only if there was an error
207 * and will have logged their own errors.
215 * getrequest: read in a full message. Timeout, in case the client died.
216 * returns: -1 = timeout or other error.
217 * positive number = message size.
227 extern void timeout();
231 DEBUG((9,"in getrequest"));
233 oldhanp
= signal(SIGALRM
, timeout
);
234 (void)alarm(ALARMTIME
);
236 /* read in MINMSGSZ to determine type of msg */
237 if ((size
= l_rcv(bp
, MINMSGSZ
, &flags
)) != MINMSGSZ
) {
238 DEBUG((9, "getrequest: l_rcv returned %d", size
));
239 tli_error(E_RCV_MSG
, CONTINUE
);
245 * if message is NLPS protocol...
248 if ((!strncmp(bp
,NLPSIDSTR
,NLPSIDSZ
)) && /* NLPS request */
249 (*(bp
+ NLPSIDSZ
) == NLPSSEPCHAR
)) {
252 if (++size
> RCVBUFSZ
) {
253 logmessage("NLPS: recieve buffer not large enough");
257 if (t_rcv(0, tmp
, sizeof(char), &flags
) != sizeof(char)) {
258 tli_error(E_RCV_MSG
, CONTINUE
);
262 } while (*tmp
++ != '\0');
267 * else if message is for the MS-NET file server...
270 } else if ( (*bp
== (char)0xff) && (!strncmp(bp
+1,SMBIDSTR
,SMBIDSZ
)) ) {
272 /* read in 28 more bytes to get count of paramter words */
273 if (l_rcv(tmp
, 28, &flags
) != 28) {
274 tli_error(E_RCV_MSG
, CONTINUE
);
281 * read amount of paramater words plus word for
282 * the number of data bytes to follow (2 bytes/word)
284 cnt
= (int)*(tmp
- 1) * 2 + 2;
286 if ((size
+= cnt
) > RCVBUFSZ
) {
287 logmessage("NLPS: recieve buffer not large enough");
291 if (l_rcv(tmp
, cnt
, &flags
) != cnt
) {
292 tli_error(E_RCV_MSG
, CONTINUE
);
297 getword(tmp
- 2, &cnt
);
299 if ((size
+= cnt
) > RCVBUFSZ
) {
300 logmessage("NLPS: recieve buffer not large enough");
304 if (l_rcv(tmp
, cnt
, &flags
) != cnt
) {
305 tli_error(E_RCV_MSG
, CONTINUE
);
312 * else, message type is unknown...
316 logmessage("NLPS: Unknown service request (ignored)");
317 DEBUG((7,"msg size: %d; 1st four chars (hex) %x %x %x %x",
318 *bp
, *(bp
+1), *(bp
+2), *(bp
+3)));
323 signal(SIGALRM
, oldhanp
);
325 DEBUG((7,"t_rcv returned %d, flags: %x",size
,flags
));
332 * The following code is for patching a 6300 side bug. The original
333 * message that comes over may contain 2 null bytes which aren't
334 * part of the message, and if left on the stream, will poison the
335 * server. Peek into the stream and snarf up those bytes if they
336 * are there. If anything goes wrong with the I_PEEK, just continue,
337 * if the nulls weren't there, it'll work, and if they were, all that
338 * will happen is that the server will fail. Just note what happened
346 register struct strpeek
*peekp
;
347 char scratch
[BUFSIZ
];
354 /* need to ask for ctl info to avoid bug in I_PEEK code */
355 peekp
->ctlbuf
.maxlen
= 1;
356 peekp
->ctlbuf
.buf
= junk
;
357 peekp
->databuf
.maxlen
= 2;
358 peekp
->databuf
.buf
= junk
;
359 ret
= ioctl(0, I_PEEK
, &peek
);
361 sprintf(scratch
, "NLPS: nullfix(): unable to PEEK, errno is %d", errno
);
362 DEBUG((9, "nullfix(): I_PEEK failed, errno is %d", errno
));
366 DEBUG((9, "nullfix(): no messages on stream to PEEK"));
369 if (peekp
->databuf
.len
== 2) {
370 /* Note: junk contains "peeked" data */
371 DEBUG((9, "peeked <%x> <%x>", junk
[0], junk
[1]));
372 if ((junk
[0] == 0) && (junk
[1] == 0)) {
373 /* pitch the nulls */
374 DEBUG((9, "pitching 2 nulls from first peek"));
375 l_rcv(junk
, 2, &flags
);
380 * this represents a somewhat pathological case where
381 * the "2 nulls" are broken across message boundaries.
382 * Pitch the first and hope the next one is there
385 else if (peekp
->databuf
.len
== 1) {
386 DEBUG((9, "peeked <%x>", junk
[0]));
388 /* pitch the first */
389 DEBUG((9, "split nulls, pitching first"));
390 l_rcv(junk
, 1, &flags
);
391 peekp
->databuf
.maxlen
= 1;
392 ret
= ioctl(0, I_PEEK
, &peek
);
394 sprintf(scratch
, "NLPS: nullfix(): unable to PEEK second time, errno is %d", errno
);
395 DEBUG((9, "second peek failed, errno %d", errno
));
399 DEBUG((9, "no messages for 2nd peek"));
402 if (peekp
->databuf
.len
== 1) {
403 DEBUG((9, "2nd peek <%x>", junk
[0]));
405 /* pitch the second */
406 DEBUG((9, "pitching 2nd single null"));
407 l_rcv(junk
, 1, &flags
);
410 /* uh oh, server will most likely fail */
411 DEBUG((9, "2nd null not found"));
412 logmessage("NLPS: nullfix(): threw away a valid null byte");
423 * timeout: SIGALRM signal handler. Invoked if t_rcv timed out.
424 * See comments about 'exit' in nlps_server().
431 DEBUG((9, "TIMEOUT"));
432 error(E_RCV_TMO
, EXIT
| NOCORE
);
438 * nls_service: Validate and start a server requested via the NLPS protocol
440 * version 0:1 -- expect "NLPS:000:001:service_code".
442 * returns only if there was an error (either msg format, or couldn't exec)
445 static char *badversion
=
446 "NLPS: Unknown version of an NLPS service request: %d:%d";
447 static char *disabledmsg
=
448 "NLPS: Request for service code <%s> denied, service is disabled";
449 static char *nlsunknown
=
450 "NLPS: Request for service code <%s> denied, unknown service code";
454 * Nlsversion can be used as a NLPS flag (< 0 == not nls service)
455 * and when >= 0, indicates the version of the NLPS protocol used
458 static int Nlsversion
= -1; /* protocol version */
461 nls_service(bp
, size
)
467 register char *svc_code_p
= svc_buf
;
470 dbf_t
*getdbfentry();
471 extern char **mkdbfargv();
475 if (nls_chkmsg(bp
, size
, &low
, &high
, svc_code_p
)) {
476 if ((low
== 0) || (low
== 2))
479 sprintf(scratch
, badversion
, low
, high
);
481 error(E_BAD_VERSION
, CONTINUE
);
485 DEBUG((9,"nls_service: protocol version %d", Nlsversion
));
488 * common code for protocol version 0 or 2
489 * version 0 allows no answerback message
490 * version 2 allows exactly 1 answerback message
493 if (dbp
= getdbfentry(svc_code_p
)) {
494 if (dbp
->dbf_flags
& DBF_OFF
) {
495 sprintf(scratch
, disabledmsg
, svc_code_p
);
497 nls_reply(NLSDISABLED
, scratch
);
500 if (dbp
->dbf_sflags
& CFLAG
) {
501 exec_cmd(dbp
, (char **)0);
502 /* return is an error */
505 sprintf(msgbuf
,"NLPS (%s) passfd: %s",
508 nls_reply(NLSSTART
, msgbuf
);
510 /* open pipe to pass fd through */
511 if ((passfd
= open(dbp
->dbf_cmd_line
,
513 sprintf(scratch
,"NLPS open failed: %s", dbp
->dbf_cmd_line
);
516 DEBUG((9, "pushmod string: %s", dbp
->dbf_modules
));
517 if (pushmod(0, dbp
->dbf_modules
)) {
518 logmessage("NLPS: Can't push server's modules: exit");
519 (void)exit(2); /* server, don't log */
522 DEBUG((9, "Running doconfig on %s", dbp
->dbf_svc_code
));
524 sprintf(msgbuf
,"%s/%s",Basedir
,dbp
->dbf_svc_code
);
526 if ((i
= doconfig(0, msgbuf
, NOASSIGN
)) != 0) {
527 DEBUG((9, "doconfig exited with code %d", i
));
528 sprintf(scratch
, "doconfig failed on line %d of script %s",
533 if (ioctl(passfd
, I_SENDFD
, 0) < 0) {
534 sprintf(scratch
,"NLPS passfd failed: %s", dbp
->dbf_cmd_line
);
541 sprintf(scratch
, nlsunknown
, svc_code_p
);
543 nls_reply(NLSUNKNOWN
, scratch
);
548 error(E_BAD_FORMAT
, CONTINUE
);
550 /* if we're still here, server didn't get exec'ed */
558 * nls_chkmsg: validate message and return fields to caller.
559 * returns: TRUE == good format
564 nls_chkmsg(bp
, size
, lowp
, highp
, svc_code_p
)
565 char *bp
, *svc_code_p
;
566 int size
, *lowp
, *highp
;
569 /* first, make sure bp is null terminated */
571 if ((*(bp
+ size
- 1)) != '\0')
574 /* scanf returns number of "matched and assigned items" */
576 return(sscanf(bp
, "%*4c:%3d:%3d:%s", lowp
, highp
, svc_code_p
) == 3);
582 * nls_reply: send the "service request response message"
583 * when appropriate. (Valid if running version 2 or greater).
584 * Must use write(2) since unknown modules may be pushed.
587 * protocol_verion_# : message_code_# : message_text
590 static char *srrpprot
= "%d:%d:%s";
593 nls_reply(int code
, char *text
)
597 /* Nlsversion = -1 for login service */
599 if (Nlsversion
>= 2) {
600 DEBUG((7, "nls_reply: sending response message"));
601 sprintf(scratch
, srrpprot
, Nlsversion
, code
, text
);
602 t_snd(0, scratch
, strlen(scratch
)+1, 0);
608 * common code to start a server process (for any service)
609 * if optional argv is given, info comes from o_argv, else pointer
610 * to dbf struct is used. In either case, first argument in argv is
611 * full pathname of server. Before exec-ing the server, the caller's
612 * logical address, opt and udata are added to the environment.
615 static char homeenv
[BUFSIZ
];
620 exec_cmd(dbf_t
*dbp
, char **o_argv
)
624 extern char **environ
;
625 dbf_t
*getdbfentry();
626 extern char **mkdbfargv();
633 * o_argv is set during SMB service setup only, in
634 * which case dbp is NULL.
639 if ((wdbp
= getdbfentry(DBF_SMB_CODE
)) == NULL
) {
640 /* this shouldn't happen because at this point we've
641 already found it once */
642 logmessage("NLPS: SMB message, missing data base entry");
643 (void)exit(2); /* server, don't log */
647 argvp
= mkdbfargv(dbp
);
650 sprintf(msgbuf
,"NLPS (%s) exec: %s",
651 (dbp
)?dbp
->dbf_svc_code
:DBF_SMB_CODE
, path
);
652 nls_reply(NLSSTART
, msgbuf
);
655 if (wdbp
->dbf_flags
& DBF_UTMP
) {
663 * create a utmpx entry. extra fork makes parent init,
664 * which will clean up the entry.
667 DEBUG((9, "Creating a utmpx entry for this service "));
668 if ((tmp
= fork()) < 0) {
669 logmessage("NLPS: Can't fork to create utmpx entry");
673 exit(0); /* kill parent */
676 * child continues processing, creating utmpx and exec'ing
681 if (fstat(0, &sbuf
) < 0) {
682 logmessage("NLPS: Stat failed on fd 0: no line "
683 "field available for utmpx entry");
688 * MPREFIX is added to the environment by the parent
691 prefix
= getenv("MPREFIX");
692 if (minor(sbuf
.st_rdev
) < 100)
693 sprintf(device
, "%.9s%02.02d", prefix
, minor(sbuf
.st_rdev
));
695 sprintf(device
, "%.8s%03.03d", prefix
, minor(sbuf
.st_rdev
));
696 DEBUG((9, "Device: %s", device
));
698 strncpy(utline
.ut_user
, wdbp
->dbf_id
,
699 sizeof (utline
.ut_user
) - 1);
700 sprintf(utline
.ut_id
, "ps%c%c", SC_WILDC
, SC_WILDC
);
701 strncpy(utline
.ut_line
, device
, sizeof (utline
.ut_line
) - 1);
702 utline
.ut_pid
= getpid();
703 utline
.ut_type
= USER_PROCESS
;
704 utline
.ut_exit
.e_termination
= 0;
705 utline
.ut_exit
.e_exit
= 0;
706 utline
.ut_xtime
= (time_t) time((time_t *)0);
710 /* after pushmod, tli calls are questionable? */
712 DEBUG((9, "pushmod string: %s", wdbp
->dbf_modules
));
713 if (dbp
&& pushmod(NETFD
, dbp
->dbf_modules
)) {
714 logmessage("NLPS: Can't push server's modules: exit");
715 exit(2); /* server, don't log */
718 DEBUG((9, "Running doconfig on %s", wdbp
->dbf_svc_code
));
719 if ((i
= doconfig(NETFD
, wdbp
->dbf_svc_code
, 0)) != 0) {
720 DEBUG((9, "doconfig exited with code %d", i
));
721 sprintf(msgbuf
, "doconfig failed on line %d of script %s",
722 i
, wdbp
->dbf_svc_code
);
727 logmessage("NLPS: No database entry");
728 exit(2); /* server, don't log */
731 if ((pwdp
= getpwnam(wdbp
->dbf_id
)) == NULL
) {
732 sprintf(msgbuf
, "NLPS: Missing or bad passwd entry for <%s>",wdbp
->dbf_id
);
734 exit(2); /* server, don't log */
738 if (setgid(pwdp
->pw_gid
)) {
739 if ((grpp
= getgrgid(pwdp
->pw_gid
)) == NULL
) {
740 sprintf(msgbuf
, "NLPS: No group entry for %ld", pwdp
->pw_gid
);
742 exit(2); /* server, don't log */
744 sprintf(msgbuf
, "NLPS: Cannot set group id to %s", grpp
->gr_name
);
746 (void)exit(2); /* server, don't log */
749 if (setuid(pwdp
->pw_uid
)) {
750 sprintf(msgbuf
, "NLPS: Cannot set user id to %s", wdbp
->dbf_id
);
752 (void)exit(2); /* server, don't log */
755 if (chdir(pwdp
->pw_dir
)) {
756 sprintf(msgbuf
, "NLPS: Cannot chdir to %s", pwdp
->pw_dir
);
758 (void)exit(2); /* server, don't log */
761 DEBUG((9, "New uid %ld New gid %ld", getuid(), getgid()));
763 sprintf(homeenv
, "HOME=%s", pwdp
->pw_dir
);
764 DEBUG((9,"HOME=%s", pwdp
->pw_dir
));
772 execve(path
, argvp
, environ
);
774 /* exec returns only on failure! */
776 logmessage("NLPS server: could not exec service");
777 sys_error(E_SYS_ERROR
, CONTINUE
);
786 * isdigits: string version of isdigit. (See ctype(3))
789 /* This routine is public here and used in lsdbf.c as an external */
794 register int flag
= 1;
807 l_rcv(bufp
, bytes
, flagp
)
813 register int count
= bytes
;
814 register char *bp
= bufp
;
816 DEBUG((9, "in l_rcv"));
820 n
= t_rcv(0, bp
, count
, flagp
);
821 DEBUG((9, "l_rcv, after t_rcv call, n = %d",n
));
824 DEBUG((9, "l_rcv, t_errno is %d", t_errno
));
826 if (t_errno
== TLOOK
) {
827 DEBUG((9, "l_rcv, t_look returns %d", t_look(0)));
841 * getdbfentry: Given a service code, return a pointer to the dbf_t
842 * entry. Return NULL if the entry doesn't exist.
843 * Reads the data base, one line at a time, into
847 static char Dbf_line_buf
[DBFLINESZ
];
848 static dbf_t Dbf_entry
;
851 getdbfentry(svc_code_p
)
852 register char *svc_code_p
;
855 char dbfname
[BUFSIZ
];
857 sprintf(dbfname
, "%s/%s", Basedir
, DBFNAME
);
858 if ((dbfp
= fopen(dbfname
, "r")) == NULL
) {
859 DEBUG((9, "open of database file %s failed", DBFNAME
));
860 logmessage("NLPS: Unable to open database file");
861 return((dbf_t
*)NULL
);
864 DEBUG((9, "database file opened, looking for %s", svc_code_p
));
865 while (rd_dbf_line(dbfp
, Dbf_line_buf
, &Dbf_entry
.dbf_svc_code
,
866 &Dbf_entry
.dbf_flags
, &Dbf_entry
.dbf_id
, &Dbf_entry
.dbf_res1
,
867 &Dbf_entry
.dbf_res2
, &Dbf_entry
.dbf_res3
,&Dbf_entry
.dbf_prv_adr
,
868 &Dbf_entry
.dbf_prognum
, &Dbf_entry
.dbf_version
,
869 &Dbf_entry
.dbf_modules
, &Dbf_entry
.dbf_sflags
,
870 &Dbf_entry
.dbf_cmd_line
) > 0) {
872 /* see if this line is the one we want (svc_code match) */
873 if (!strcmp(Dbf_entry
.dbf_svc_code
, svc_code_p
)) {
879 DEBUG((9, "No svc code match"));
881 return((dbf_t
*)0); /* svc code not in database */