2 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 /* Copyright(c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
10 * Copyright (c) 1983 The Regents of the University of California.
11 * All rights reserved.
13 * Redistribution and use in source and binary forms are permitted
14 * provided that the above copyright notice and this paragraph are
15 * duplicated in all such forms and that any documentation,
16 * advertising materials, and other materials related to such
17 * distribution and use acknowledge that the software was developed
18 * by the University of California, Berkeley. The name of the
19 * University may not be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
23 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27 * remote login server:
35 #include <sys/types.h>
37 #include <sys/socket.h>
40 #include <netinet/in.h>
53 #include <sac.h> /* for SC_WILDC */
55 #include <sys/filio.h>
56 #include <sys/logindmux.h>
57 #include <sys/rlioctl.h>
58 #include <sys/termios.h>
59 #include <sys/tihdr.h>
60 #include <arpa/inet.h>
61 #include <security/pam_appl.h>
66 #include <krb5_repository.h>
67 #include <sys/cryptmod.h>
69 #include <addr_match.h>
70 #include <store_forw_creds.h>
72 #define KRB5_RECVAUTH_V5 5
73 #define UT_NAMESIZE sizeof (((struct utmpx *)0)->ut_name)
75 static char lusername
[UT_NAMESIZE
+1];
76 static char rusername
[UT_NAMESIZE
+1];
77 static char *krusername
= NULL
;
80 static krb5_ccache ccache
= NULL
;
81 static krb5_keyblock
*session_key
= NULL
;
82 static int chksum_flag
= 0;
83 static int use_auth
= 0;
84 static enum kcmd_proto kcmd_protocol
;
86 static krb5_data encr_iv
= { NULL
, 0 };
87 static krb5_data decr_iv
= { NULL
, 0 };
88 #endif /* ALLOW_KCMD_V2 */
90 #define CHKSUM_REQUIRED 0x01
91 #define CHKSUM_IGNORED 0x02
92 #define VALID_CHKSUM(x) ((x) == 0 || (x) == CHKSUM_REQUIRED ||\
93 (x) == CHKSUM_IGNORED)
95 #define PWD_IF_FAIL 0x01
96 #define PWD_REQUIRED 0x02
98 #define AUTH_NONE 0x00
100 #define ARGSTR "k5exEXciM:s:S:D:"
101 #define DEFAULT_TOS 16
103 #define KRB5_PROG_NAME "krlogin"
105 #define SECURE_MSG "This rlogin session is using encryption " \
106 "for all data transmissions.\r\n"
108 #define KRB_V5_SENDAUTH_VERS "KRB5_SENDAUTH_V1.0"
109 #define KRB5_RECVAUTH_V5 5
111 static krb5_error_code
krb5_compat_recvauth(krb5_context context
,
112 krb5_auth_context
*auth_context
,
114 krb5_principal server
,
117 krb5_ticket
**ticket
,
118 krb5_int32
*auth_sys
,
121 static void do_krb_login(int, char *, char *, krb5_context
, int, krb5_keytab
);
122 static int configure_stream(int, krb5_keyblock
*, int, krb5_data
*, uint_t
);
124 extern krb5_error_code
krb5_read_message(krb5_context
, krb5_pointer
,
126 extern krb5_error_code
krb5_net_read(krb5_context
, int, char *, int);
128 #define LOGIN_PROGRAM "/bin/login"
130 #define DEFAULT_PROG_NAME "rlogin"
132 static const char *pam_prog_name
= DEFAULT_PROG_NAME
;
133 static void rmut(void);
134 static void doit(int, struct sockaddr_storage
*, krb5_context
, int,
136 static void protocol(int, int, int);
138 static int readstream(int, char *, int);
139 static void fatal(int, const char *);
140 static void fatalperror(int, const char *);
141 static int send_oob(int fd
, void *ptr
, size_t count
);
142 static int removemod(int f
, char *modname
);
149 if (fstat(fd
, &stats
) == -1)
151 return (S_ISSOCK(stats
.st_mode
));
155 * audit_rlogin_settid stores the terminal id while it is still
156 * available. Subsequent calls to adt_load_hostname() return
157 * the id which is stored here.
160 audit_rlogin_settid(int fd
) {
161 adt_session_data_t
*ah
;
162 adt_termid_t
*termid
;
165 if ((rc
= adt_start_session(&ah
, NULL
, 0)) == 0) {
166 if ((rc
= adt_load_termid(fd
, &termid
)) == 0) {
167 if ((rc
= adt_set_user(ah
, ADT_NO_AUDIT
,
168 ADT_NO_AUDIT
, 0, ADT_NO_AUDIT
,
169 termid
, ADT_SETTID
)) == 0)
170 (void) adt_set_proc(ah
);
173 (void) adt_end_session(ah
);
181 main(int argc
, char *argv
[])
185 struct sockaddr_storage from
;
191 krb5_context krb_context
;
192 krb5_keytab keytab
= NULL
;
193 krb5_error_code status
;
195 char *keytab_file
= NULL
;
197 struct sockaddr_storage ouraddr
;
202 openlog("rlogind", LOG_PID
| LOG_ODELAY
, LOG_DAEMON
);
204 while ((c
= getopt(argc
, argv
, ARGSTR
)) != -1) {
208 use_auth
= KRB5_RECVAUTH_V5
;
217 realm
= (char *)strdup(optarg
);
220 keytab_file
= (char *)strdup(optarg
);
223 chksum_flag
|= CHKSUM_REQUIRED
;
226 chksum_flag
|= CHKSUM_IGNORED
;
229 if (optarg
== NULL
|| (tos
= atoi(optarg
)) < 0 ||
231 syslog(LOG_ERR
, "%s: illegal tos value: "
232 "%s\n", argv
[0], optarg
);
240 debug_port
= atoi(optarg
);
244 syslog(LOG_ERR
, "Unrecognized command line option "
245 "(-%c), exiting", optopt
);
249 if (use_auth
== KRB5_RECVAUTH_V5
) {
250 status
= krb5_init_context(&krb_context
);
252 syslog(LOG_ERR
, "Error initializing krb5: %s",
253 error_message(status
));
257 (void) krb5_set_default_realm(krb_context
, realm
);
258 if (keytab_file
!= NULL
) {
259 if ((status
= krb5_kt_resolve(krb_context
,
264 "while resolving srvtab file %s",
274 struct sockaddr_in sin
;
276 if ((s
= socket(AF_INET
, SOCK_STREAM
, PF_UNSPEC
)) < 0) {
277 fatalperror(STDERR_FILENO
, "Error in socket");
280 (void) memset((char *)&sin
, 0, sizeof (sin
));
281 sin
.sin_family
= AF_INET
;
282 sin
.sin_port
= htons(debug_port
);
283 sin
.sin_addr
.s_addr
= INADDR_ANY
;
285 (void) setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
286 (char *)&on
, sizeof (on
));
288 if ((bind(s
, (struct sockaddr
*)&sin
, sizeof (sin
))) < 0) {
289 fatalperror(STDERR_FILENO
, "bind error");
292 if ((listen(s
, 5)) < 0) {
293 fatalperror(STDERR_FILENO
, "listen error");
296 fromlen
= sizeof (from
);
297 if ((fd
= accept(s
, (struct sockaddr
*)&from
, &fromlen
)) < 0) {
298 fatalperror(STDERR_FILENO
, "accept error");
305 if (!issock(STDIN_FILENO
))
307 "stdin is not a socket file descriptor");
311 fromlen
= sizeof (from
);
312 if (getpeername(fd
, (struct sockaddr
*)&from
, &fromlen
) < 0)
313 fatalperror(STDERR_FILENO
, "getpeername");
315 if (audit_rlogin_settid(fd
)) /* set terminal ID */
316 fatalperror(STDERR_FILENO
, "audit");
318 if (setsockopt(fd
, SOL_SOCKET
, SO_KEEPALIVE
, (char *)&on
,
320 syslog(LOG_WARNING
, "setsockopt(SO_KEEPALIVE): %m");
322 if (!VALID_CHKSUM(chksum_flag
)) {
323 syslog(LOG_ERR
, "Configuration error: mutually exclusive "
324 "options specified (-c and -i)");
325 fatal(fd
, "Checksums are required and ignored (-c and -i);"
326 "these options are mutually exclusive - check "
327 "the documentation.");
329 ourlen
= sizeof (ouraddr
);
330 if (getsockname(fd
, (struct sockaddr
*)&ouraddr
, &ourlen
) == -1) {
331 syslog(LOG_ERR
, "getsockname error: %m");
336 ouraddr
.ss_family
!= AF_INET6
&&
337 setsockopt(fd
, IPPROTO_IP
, IP_TOS
, (char *)&tos
,
339 errno
!= ENOPROTOOPT
) {
340 syslog(LOG_ERR
, "setsockopt(IP_TOS %d): %m", tos
);
342 doit(fd
, &from
, krb_context
, encr_flag
, keytab
);
346 static void cleanup(int);
347 static int nsize
= 0; /* bytes read prior to pushing rlmod */
348 static char *rlbuf
; /* buffer where nbytes are read to */
351 static struct winsize win
= { 0, 0, 0, 0 };
353 static char hostname
[MAXHOSTNAMELEN
+ 1];
356 getstr(int f
, char *buf
, int cnt
, char *err
)
360 if (read(f
, &c
, 1) != 1 || (--cnt
< 0)) {
361 syslog(LOG_ERR
, "Error reading \'%s\' field", err
);
368 static krb5_error_code
370 krb5_context krb_context
,
371 unsigned int *valid_checksum
,
372 krb5_ticket
**ticket
,
374 krb5_principal
*client
,
378 krb5_error_code status
= 0;
379 krb5_auth_context auth_context
= NULL
;
381 krb5_authenticator
*authenticator
;
383 krb5_data auth_version
;
387 if ((status
= krb5_auth_con_init(krb_context
, &auth_context
)))
390 /* Only need remote address for rd_cred() to verify client */
391 if ((status
= krb5_auth_con_genaddrs(krb_context
, auth_context
, f
,
392 KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR
)))
395 status
= krb5_auth_con_getrcache(krb_context
, auth_context
, &rcache
);
400 krb5_principal server
;
402 status
= krb5_sname_to_principal(krb_context
, 0, 0,
403 KRB5_NT_SRV_HST
, &server
);
407 status
= krb5_get_server_rcache(krb_context
,
408 krb5_princ_component(krb_context
, server
, 0),
410 krb5_free_principal(krb_context
, server
);
414 status
= krb5_auth_con_setrcache(krb_context
, auth_context
,
419 if ((status
= krb5_compat_recvauth(krb_context
,
422 NULL
, /* Specify daemon principal */
424 keytab
, /* NULL to use v5srvtab */
425 ticket
, /* return ticket */
426 auth_type
, /* authentication system */
428 if (*auth_type
== KRB5_RECVAUTH_V5
) {
430 * clean up before exiting
432 getstr(f
, rusername
, sizeof (rusername
), "remuser");
433 getstr(f
, lusername
, sizeof (lusername
), "locuser");
434 getstr(f
, term
, sizeof (term
), "Terminal type");
439 getstr(f
, lusername
, sizeof (lusername
), "locuser");
440 getstr(f
, term
, sizeof (term
), "Terminal type");
442 kcmd_protocol
= KCMD_UNKNOWN_PROTOCOL
;
443 if (auth_version
.length
!= 9 || auth_version
.data
== NULL
) {
444 syslog(LOG_ERR
, "Bad application protocol version length in "
445 "KRB5 exchange, exiting");
446 fatal(f
, "Bad application version length, exiting.");
449 * Determine which Kerberos CMD protocol was used.
451 if (strncmp(auth_version
.data
, "KCMDV0.1", 9) == 0) {
452 kcmd_protocol
= KCMD_OLD_PROTOCOL
;
453 } else if (strncmp(auth_version
.data
, "KCMDV0.2", 9) == 0) {
454 kcmd_protocol
= KCMD_NEW_PROTOCOL
;
456 syslog(LOG_ERR
, "Unrecognized KCMD protocol (%s), exiting",
457 (char *)auth_version
.data
);
458 fatal(f
, "Unrecognized KCMD protocol, exiting");
461 if ((*auth_type
== KRB5_RECVAUTH_V5
) && chksum_flag
&&
462 kcmd_protocol
== KCMD_OLD_PROTOCOL
) {
463 if ((status
= krb5_auth_con_getauthenticator(krb_context
,
467 if (authenticator
->checksum
) {
468 struct sockaddr_storage adr
;
469 int adr_length
= sizeof (adr
);
476 * Define the lenght of the chksum buffer.
477 * chksum string = "[portnum]:termstr:username"
478 * The extra 32 is to hold a integer string for
481 buflen
= strlen(term
) + strlen(lusername
) + 32;
482 chksumbuf
= (char *)malloc(buflen
);
483 if (chksumbuf
== 0) {
484 krb5_free_authenticator(krb_context
,
486 fatal(f
, "Out of memory error");
489 if (getsockname(f
, (struct sockaddr
*)&adr
,
491 krb5_free_authenticator(krb_context
,
493 fatal(f
, "getsockname error");
496 (void) snprintf(chksumbuf
, buflen
,
498 ntohs(SOCK_PORT(adr
)),
501 input
.data
= chksumbuf
;
502 input
.length
= strlen(chksumbuf
);
503 key
.contents
= (*ticket
)->enc_part2
->session
->contents
;
504 key
.length
= (*ticket
)->enc_part2
->session
->length
;
505 status
= krb5_c_verify_checksum(krb_context
,
508 authenticator
->checksum
,
511 if (status
== 0 && *valid_checksum
== 0)
512 status
= KRB5KRB_AP_ERR_BAD_INTEGRITY
;
515 krb5_xfree(chksumbuf
);
517 krb5_free_authenticator(krb_context
,
522 krb5_free_authenticator(krb_context
, authenticator
);
525 if ((status
= krb5_copy_principal(krb_context
,
526 (*ticket
)->enc_part2
->client
,
530 /* Get the Unix username of the remote user */
531 getstr(f
, rusername
, sizeof (rusername
), "remuser");
533 /* Get the Kerberos principal name string of the remote user */
534 if ((status
= krb5_unparse_name(krb_context
, *client
, &krusername
)))
538 syslog(LOG_DEBUG
| LOG_AUTH
, "rlogind: got krb5 credentials for %s",
539 (krusername
!= NULL
? krusername
: "<unknown>"));
543 status
= krb5_auth_con_getremotesubkey(krb_context
,
547 syslog(LOG_ERR
, "Error getting KRB5 session "
549 fatal(f
, "Error getting KRB5 session subkey, exiting");
552 * The "new" protocol requires that a subkey be sent.
554 if (session_key
== NULL
&&
555 kcmd_protocol
== KCMD_NEW_PROTOCOL
) {
556 syslog(LOG_ERR
, "No KRB5 session subkey sent, exiting");
557 fatal(f
, "No KRB5 session subkey sent, exiting");
560 * The "old" protocol does not permit an authenticator subkey.
561 * The key is taken from the ticket instead (see below).
563 if (session_key
!= NULL
&&
564 kcmd_protocol
== KCMD_OLD_PROTOCOL
) {
565 syslog(LOG_ERR
, "KRB5 session subkey not permitted "
566 "with old KCMD protocol, exiting");
568 fatal(f
, "KRB5 session subkey not permitted "
569 "with old KCMD protocol, exiting");
572 * If no key at this point, use the session key from
575 if (session_key
== NULL
) {
577 * Save the session key so we can configure the crypto
580 status
= krb5_copy_keyblock(krb_context
,
581 (*ticket
)->enc_part2
->session
,
584 syslog(LOG_ERR
, "krb5_copy_keyblock failed");
585 fatal(f
, "krb5_copy_keyblock failed");
589 * If session key still cannot be found, we must
590 * exit because encryption is required here
591 * when encr_flag (-x) is set.
593 if (session_key
== NULL
) {
594 syslog(LOG_ERR
, "Could not find an encryption key,"
596 fatal(f
, "Encryption required but key not found, "
601 * Use krb5_read_message to read the principal stuff.
603 if ((status
= krb5_read_message(krb_context
, (krb5_pointer
)&f
,
605 fatal(f
, "Error reading krb5 message");
607 if (inbuf
.length
) { /* Forwarding being done, read creds */
608 krb5_creds
**creds
= NULL
;
610 if (status
= krb5_rd_cred(krb_context
, auth_context
, &inbuf
,
613 (void) krb5_rc_close(krb_context
, rcache
);
614 krb5_free_creds(krb_context
, *creds
);
615 fatal(f
, "Can't get forwarded credentials");
618 /* Store the forwarded creds in the ccache */
619 if (status
= store_forw_creds(krb_context
,
620 creds
, *ticket
, lusername
,
623 (void) krb5_rc_close(krb_context
, rcache
);
624 krb5_free_creds(krb_context
, *creds
);
625 fatal(f
, "Can't store forwarded credentials");
627 krb5_free_creds(krb_context
, *creds
);
631 (void) krb5_rc_close(krb_context
, rcache
);
637 do_krb_login(int f
, char *host_addr
, char *hostname
,
638 krb5_context krb_context
, int encr_flag
,
641 krb5_error_code status
;
642 uint_t valid_checksum
;
643 krb5_ticket
*ticket
= NULL
;
646 krb5_principal client
= NULL
;
649 fatal(f
, "Error authorizing KRB5 connection, "
650 "server lacks privilege");
652 status
= recvauth(f
, krb_context
, &valid_checksum
, &ticket
,
653 &auth_sys
, &client
, encr_flag
, keytab
);
656 krb5_free_ticket(krb_context
, ticket
);
659 "Authentication failed from %s(%s): %s\n",
660 host_addr
, hostname
, error_message(status
));
661 fatal(f
, "Kerberos authentication failed, exiting");
664 if (auth_sys
!= KRB5_RECVAUTH_V5
) {
665 fatal(f
, "This server only supports Kerberos V5");
668 * Authenticated OK, now check authorization.
670 if (client
&& krb5_kuserok(krb_context
, client
, lusername
))
671 auth_sent
= KRB5_RECVAUTH_V5
;
674 if (auth_sent
== KRB5_RECVAUTH_V5
&&
675 kcmd_protocol
== KCMD_OLD_PROTOCOL
&&
676 chksum_flag
== CHKSUM_REQUIRED
&& !valid_checksum
) {
677 syslog(LOG_ERR
, "Client did not supply required checksum, "
678 "connection rejected.");
679 fatal(f
, "Client did not supply required checksum, "
680 "connection rejected.");
683 if (auth_sys
!= auth_sent
) {
684 char *msg_fail
= NULL
;
688 krb5_free_ticket(krb_context
, ticket
);
690 if (krusername
!= NULL
) {
692 * msgsize must be enough to hold
693 * krusername, lusername and a brief
694 * message describing the failure.
696 msgsize
= strlen(krusername
) +
697 strlen(lusername
) + 80;
698 msg_fail
= (char *)malloc(msgsize
);
700 if (msg_fail
== NULL
) {
701 syslog(LOG_ERR
, "User is not authorized to login to "
702 "specified account");
704 fatal(f
, "User is not authorized to login to "
705 "specified account");
708 (void) snprintf(msg_fail
, msgsize
,
709 "Access denied because of improper "
712 (void) snprintf(msg_fail
, msgsize
,
713 "User %s is not authorized to login "
715 krusername
, lusername
);
716 syslog(LOG_ERR
, "%s", msg_fail
);
724 * Utility routine to send a CRYPTIOCSTOP ioctl to the
725 * crypto module(cryptmod).
728 stop_stream(int fd
, int dir
)
730 struct strioctl crioc
;
731 uint32_t stopdir
= dir
;
733 crioc
.ic_cmd
= CRYPTIOCSTOP
;
734 crioc
.ic_timout
= -1;
735 crioc
.ic_len
= sizeof (stopdir
);
736 crioc
.ic_dp
= (char *)&stopdir
;
738 if (ioctl(fd
, I_STR
, &crioc
))
739 syslog(LOG_ERR
, "Error sending CRYPTIOCSTOP ioctl: %m");
745 * Utility routine to send a CRYPTIOCSTART ioctl to the
746 * crypto module(cryptmod). This routine may contain optional
747 * payload data that the cryptmod will interpret as bytes that
748 * need to be decrypted and sent back up to the application
749 * via the data stream.
752 start_stream(int fd
, int dir
)
754 struct strioctl crioc
;
759 if (dir
== CRYPT_DECRYPT
) {
760 iocval
= CRYPTIOCSTARTDEC
;
762 /* Look for data not yet processed */
763 if (ioctl(fd
, I_NREAD
, &datalen
) < 0) {
764 syslog(LOG_ERR
, "I_NREAD returned error %m");
768 data
= malloc(datalen
);
770 int nbytes
= read(fd
, data
, datalen
);
774 "malloc error (%d bytes)",
783 iocval
= CRYPTIOCSTARTENC
;
786 crioc
.ic_cmd
= iocval
;
787 crioc
.ic_timout
= -1;
788 crioc
.ic_len
= datalen
;
791 if (ioctl(fd
, I_STR
, &crioc
))
792 syslog(LOG_ERR
, "Error sending CRYPTIOCSTART ioctl: %m");
799 configure_stream(int fd
, krb5_keyblock
*skey
, int dir
, krb5_data
*ivec
,
802 struct cr_info_t setup_info
;
803 struct strioctl crioc
;
806 switch (skey
->enctype
) {
807 case ENCTYPE_DES_CBC_CRC
:
808 setup_info
.crypto_method
= CRYPT_METHOD_DES_CBC_CRC
;
810 case ENCTYPE_DES_CBC_MD5
:
811 setup_info
.crypto_method
= CRYPT_METHOD_DES_CBC_MD5
;
813 case ENCTYPE_DES_CBC_RAW
:
814 setup_info
.crypto_method
= CRYPT_METHOD_DES_CBC_NULL
;
816 case ENCTYPE_DES3_CBC_SHA1
:
817 setup_info
.crypto_method
= CRYPT_METHOD_DES3_CBC_SHA1
;
819 case ENCTYPE_ARCFOUR_HMAC
:
820 setup_info
.crypto_method
= CRYPT_METHOD_ARCFOUR_HMAC_MD5
;
822 case ENCTYPE_ARCFOUR_HMAC_EXP
:
823 setup_info
.crypto_method
= CRYPT_METHOD_ARCFOUR_HMAC_MD5_EXP
;
825 case ENCTYPE_AES128_CTS_HMAC_SHA1_96
:
826 setup_info
.crypto_method
= CRYPT_METHOD_AES128
;
828 case ENCTYPE_AES256_CTS_HMAC_SHA1_96
:
829 setup_info
.crypto_method
= CRYPT_METHOD_AES256
;
832 syslog(LOG_ERR
, "Enctype in kerberos session key "
833 "is not supported by crypto module(%d)",
837 if (ivec
== NULL
|| ivec
->length
== 0) {
838 (void) memset(&setup_info
.ivec
, 0, sizeof (setup_info
.ivec
));
840 if (skey
->enctype
!= ENCTYPE_ARCFOUR_HMAC
&&
841 skey
->enctype
!= ENCTYPE_ARCFOUR_HMAC_EXP
)
842 /* Kerberos IVs are 8 bytes long for DES keys */
843 setup_info
.iveclen
= KRB5_MIT_DES_KEYSIZE
;
845 setup_info
.iveclen
= 0;
847 (void) memcpy(&setup_info
.ivec
, ivec
->data
, ivec
->length
);
848 setup_info
.iveclen
= ivec
->length
;
851 setup_info
.ivec_usage
= iv_usage
;
852 (void) memcpy(&setup_info
.key
, skey
->contents
, skey
->length
);
854 setup_info
.keylen
= skey
->length
;
855 setup_info
.direction_mask
= dir
;
857 * R* commands get special handling by crypto module -
858 * 4 byte length field is used before each encrypted block
861 setup_info
.option_mask
= (kcmd_protocol
== KCMD_OLD_PROTOCOL
?
862 CRYPTOPT_RCMD_MODE_V1
:
863 CRYPTOPT_RCMD_MODE_V2
);
865 crioc
.ic_cmd
= CRYPTIOCSETUP
;
866 crioc
.ic_timout
= -1;
867 crioc
.ic_len
= sizeof (setup_info
);
868 crioc
.ic_dp
= (char *)&setup_info
;
870 if (ioctl(fd
, I_STR
, &crioc
)) {
871 syslog(LOG_ERR
, "Error sending CRYPTIOCSETUP ioctl: %m");
877 static krb5_error_code
878 krb5_compat_recvauth(krb5_context context
,
879 krb5_auth_context
*auth_context
,
880 krb5_pointer fdp
, /* IN */
881 krb5_principal server
, /* IN */
882 krb5_int32 flags
, /* IN */
883 krb5_keytab keytab
, /* IN */
884 krb5_ticket
**ticket
, /* OUT */
885 krb5_int32
*auth_sys
, /* OUT */
886 krb5_data
*version
) /* OUT */
892 int fd
= *((int *)fdp
);
894 if ((retval
= krb5_net_read(context
, fd
, (char *)&vlen
, 4)) != 4)
895 return ((retval
< 0) ? errno
: ECONNABORTED
);
898 * Assume that we're talking to a V5 recvauth; read in the
899 * the version string, and make sure it matches.
901 len
= (int)ntohl(vlen
);
903 if (len
< 0 || len
> 255)
904 return (KRB5_SENDAUTH_BADAUTHVERS
);
910 length
= krb5_net_read(context
, fd
, buf
, len
);
913 return ((len
< 0) ? errno
: ECONNABORTED
);
916 if (strcmp(buf
, KRB_V5_SENDAUTH_VERS
) != 0) {
918 return (KRB5_SENDAUTH_BADAUTHVERS
);
922 *auth_sys
= KRB5_RECVAUTH_V5
;
924 retval
= krb5_recvauth_version(context
, auth_context
, fdp
,
925 server
, flags
| KRB5_RECVAUTH_SKIP_VERSION
,
926 keytab
, ticket
, version
);
934 struct sockaddr_storage
*fromp
,
935 krb5_context krb_context
,
941 char abuf
[INET6_ADDRSTRLEN
];
942 struct sockaddr_in
*sin
;
943 struct sockaddr_in6
*sin6
;
949 char rhost_addra
[INET6_ADDRSTRLEN
];
951 if (!(rlbuf
= malloc(BUFSIZ
))) {
952 syslog(LOG_ERR
, "rlbuf malloc failed\n");
956 if (read(f
, &c
, 1) != 1 || c
!= 0) {
957 syslog(LOG_ERR
, "failed to receive protocol zero byte\n");
961 if (fromp
->ss_family
== AF_INET
) {
962 sin
= (struct sockaddr_in
*)fromp
;
963 port
= sin
->sin_port
= ntohs((ushort_t
)sin
->sin_port
);
964 fromplen
= sizeof (struct sockaddr_in
);
966 if (!inet_ntop(AF_INET
, &sin
->sin_addr
,
967 rhost_addra
, sizeof (rhost_addra
)))
969 } else if (fromp
->ss_family
== AF_INET6
) {
970 sin6
= (struct sockaddr_in6
*)fromp
;
971 port
= sin6
->sin6_port
= ntohs((ushort_t
)sin6
->sin6_port
);
972 fromplen
= sizeof (struct sockaddr_in6
);
974 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
975 struct in_addr ipv4_addr
;
977 IN6_V4MAPPED_TO_INADDR(&sin6
->sin6_addr
,
979 if (!inet_ntop(AF_INET
, &ipv4_addr
, rhost_addra
,
980 sizeof (rhost_addra
)))
983 if (!inet_ntop(AF_INET6
, &sin6
->sin6_addr
,
984 rhost_addra
, sizeof (rhost_addra
)))
988 syslog(LOG_ERR
, "unknown address family %d\n",
990 fatal(f
, "Permission denied");
994 * Allow connections only from the "ephemeral" reserved
995 * ports(ports 512 - 1023) by checking the remote port
996 * because other utilities(e.g. in.ftpd) can be used to
997 * allow a unprivileged user to originate a connection
998 * from a privileged port and provide untrustworthy
1001 bad_port
= (use_auth
!= KRB5_RECVAUTH_V5
&&
1002 (port
>= (in_port_t
)IPPORT_RESERVED
) ||
1003 (port
< (in_port_t
)(IPPORT_RESERVED
/2)));
1004 no_name
= getnameinfo((const struct sockaddr
*) fromp
,
1005 fromplen
, hostname
, sizeof (hostname
),
1008 if (no_name
|| bad_port
) {
1009 (void) strlcpy(abuf
, rhost_addra
, sizeof (abuf
));
1010 /* If no host name, use IP address for name later on. */
1012 (void) strlcpy(hostname
, abuf
, sizeof (hostname
));
1017 * Even if getnameinfo() succeeded, we still have to check
1020 check_address("rlogind", fromp
, sin
, sin6
, rhost_addra
,
1021 hostname
, sizeof (hostname
));
1027 "connection from %s - bad port\n",
1031 "connection from %s(%s) - bad port\n",
1033 fatal(f
, "Permission denied");
1036 if (use_auth
== KRB5_RECVAUTH_V5
) {
1037 do_krb_login(f
, rhost_addra
, hostname
,
1038 krb_context
, encr_flag
, keytab
);
1039 if (krusername
!= NULL
&& strlen(krusername
)) {
1041 * Kerberos Authentication succeeded,
1042 * so set the proper program name to use
1043 * with pam (important during 'cleanup'
1046 pam_prog_name
= KRB5_PROG_NAME
;
1050 if (write(f
, "", 1) != 1) {
1052 "send of the zero byte(to %s) failed:"
1053 " cannot start data transfer mode\n",
1054 (no_name
? abuf
: hostname
));
1057 if ((p
= open("/dev/ptmx", O_RDWR
)) == -1)
1058 fatalperror(f
, "cannot open /dev/ptmx");
1059 if (grantpt(p
) == -1)
1060 fatal(f
, "could not grant slave pty");
1061 if (unlockpt(p
) == -1)
1062 fatal(f
, "could not unlock slave pty");
1063 if ((line
= ptsname(p
)) == NULL
)
1064 fatal(f
, "could not enable slave pty");
1065 if ((t
= open(line
, O_RDWR
)) == -1)
1066 fatal(f
, "could not open slave pty");
1067 if (ioctl(t
, I_PUSH
, "ptem") == -1)
1068 fatalperror(f
, "ioctl I_PUSH ptem");
1069 if (ioctl(t
, I_PUSH
, "ldterm") == -1)
1070 fatalperror(f
, "ioctl I_PUSH ldterm");
1071 if (ioctl(t
, I_PUSH
, "ttcompat") == -1)
1072 fatalperror(f
, "ioctl I_PUSH ttcompat");
1074 * POP the sockmod and push the rlmod module.
1076 * Note that sockmod has to be removed since readstream assumes
1077 * a "raw" TPI endpoint(e.g. it uses getmsg).
1079 if (removemod(f
, "sockmod") < 0)
1080 fatalperror(f
, "couldn't remove sockmod");
1083 if (ioctl(f
, I_PUSH
, "cryptmod") < 0)
1084 fatalperror(f
, "ioctl I_PUSH rlmod");
1088 if (ioctl(f
, I_PUSH
, "rlmod") < 0)
1089 fatalperror(f
, "ioctl I_PUSH rlmod");
1093 * Make sure rlmod will pass unrecognized IOCTLs to cryptmod
1095 uchar_t passthru
= 1;
1096 struct strioctl rlmodctl
;
1098 rlmodctl
.ic_cmd
= CRYPTPASSTHRU
;
1099 rlmodctl
.ic_timout
= -1;
1100 rlmodctl
.ic_len
= sizeof (uchar_t
);
1101 rlmodctl
.ic_dp
= (char *)&passthru
;
1103 if (ioctl(f
, I_STR
, &rlmodctl
) < 0)
1104 fatal(f
, "ioctl CRYPTPASSTHRU failed\n");
1108 * readstream will do a getmsg till it receives
1109 * M_PROTO type T_DATA_REQ from rloginmodopen()
1110 * indicating all data on the stream prior to pushing rlmod has
1111 * been drained at the stream head.
1113 if ((nsize
= readstream(f
, rlbuf
, BUFSIZ
)) < 0)
1114 fatalperror(f
, "readstream failed");
1116 * Make sure the pty doesn't modify the strings passed
1117 * to login as part of the "rlogin protocol." The login
1118 * program should set these flags to apropriate values
1119 * after it has read the strings.
1121 if (ioctl(t
, TCGETS
, &tp
) == -1)
1122 fatalperror(f
, "ioctl TCGETS");
1123 tp
.c_lflag
&= ~(ECHO
|ICANON
);
1124 tp
.c_oflag
&= ~(XTABS
|OCRNL
);
1125 tp
.c_iflag
&= ~(IGNPAR
|ICRNL
);
1126 if (ioctl(t
, TCSETS
, &tp
) == -1)
1127 fatalperror(f
, "ioctl TCSETS");
1130 * System V ptys allow the TIOC{SG}WINSZ ioctl to be
1131 * issued on the master side of the pty. Luckily, that's
1132 * the only tty ioctl we need to do do, so we can close the
1133 * slave side in the parent process after the fork.
1135 (void) ioctl(p
, TIOCSWINSZ
, &win
);
1139 fatalperror(f
, "fork");
1144 /* System V login expects a utmp entry to already be there */
1145 (void) memset(&ut
, 0, sizeof (ut
));
1146 (void) strncpy(ut
.ut_user
, ".rlogin", sizeof (ut
.ut_user
));
1147 (void) strncpy(ut
.ut_line
, line
, sizeof (ut
.ut_line
));
1148 ut
.ut_pid
= getpid();
1150 ut
.ut_id
[1] = (char)SC_WILDC
;
1151 ut
.ut_id
[2] = (char)SC_WILDC
;
1152 ut
.ut_id
[3] = (char)SC_WILDC
;
1153 ut
.ut_type
= LOGIN_PROCESS
;
1154 ut
.ut_exit
.e_termination
= 0;
1155 ut
.ut_exit
.e_exit
= 0;
1156 (void) time(&ut
.ut_tv
.tv_sec
);
1157 if (makeutx(&ut
) == NULL
)
1158 syslog(LOG_INFO
, "in.rlogind:\tmakeutx failed");
1160 /* controlling tty */
1162 fatalperror(f
, "setsid");
1163 if ((tt
= open(line
, O_RDWR
)) == -1)
1164 fatalperror(f
, "could not re-open slave pty");
1167 fatalperror(f
, "error closing pty master");
1169 fatalperror(f
, "error closing pty slave"
1170 " opened before session established");
1172 * If this fails we may or may not be able to output an
1176 fatalperror(f
, "error closing deamon stdout");
1177 if (dup2(tt
, STDIN_FILENO
) == -1 ||
1178 dup2(tt
, STDOUT_FILENO
) == -1 ||
1179 dup2(tt
, STDERR_FILENO
) == -1)
1180 exit(EXIT_FAILURE
); /* Disaster! No stderr! */
1184 if (use_auth
== KRB5_RECVAUTH_V5
&&
1185 krusername
!= NULL
&& strlen(krusername
)) {
1186 (void) execl(LOGIN_PROGRAM
, "login",
1189 "-u", krusername
, /* KRB5 principal name */
1190 "-s", pam_prog_name
,
1191 "-t", term
, /* Remote Terminal */
1192 "-U", rusername
, /* Remote User */
1193 "-R", KRB5_REPOSITORY_NAME
,
1194 lusername
, /* local user */
1197 (void) execl(LOGIN_PROGRAM
, "login",
1203 fatalperror(STDERR_FILENO
, "/bin/login");
1207 (void) ioctl(f
, FIONBIO
, &on
);
1208 (void) ioctl(p
, FIONBIO
, &on
);
1211 * Must ignore SIGTTOU, otherwise we'll stop
1212 * when we try and set slave pty's window shape
1213 * (our controlling tty is the master pty).
1214 * Likewise, we don't want any of the tty-generated
1215 * signals from chars passing through.
1217 (void) sigset(SIGTSTP
, SIG_IGN
);
1218 (void) sigset(SIGINT
, SIG_IGN
);
1219 (void) sigset(SIGQUIT
, SIG_IGN
);
1220 (void) sigset(SIGTTOU
, SIG_IGN
);
1221 (void) sigset(SIGTTIN
, SIG_IGN
);
1222 (void) sigset(SIGCHLD
, cleanup
);
1226 krb5_data ivec
, *ivptr
;
1228 stop_stream(f
, CRYPT_ENCRYPT
|CRYPT_DECRYPT
);
1231 * Configure the STREAMS crypto module. For now,
1232 * don't use any IV parameter. KCMDV0.2 support
1233 * will require the use of Initialization Vectors
1234 * for both encrypt and decrypt modes.
1236 if (kcmd_protocol
== KCMD_OLD_PROTOCOL
) {
1237 if (session_key
->enctype
== ENCTYPE_DES_CBC_CRC
) {
1239 * This is gross but necessary for MIT compat.
1241 ivec
.length
= session_key
->length
;
1242 ivec
.data
= (char *)session_key
->contents
;
1243 ivec_usage
= IVEC_REUSE
;
1246 ivptr
= NULL
; /* defaults to all 0's */
1247 ivec_usage
= IVEC_NEVER
;
1250 * configure both sides of stream together
1251 * since they share the same IV.
1252 * This is what makes the OLD KCMD protocol
1253 * less secure than the newer one - Bad ivecs.
1255 if (configure_stream(f
, session_key
,
1256 CRYPT_ENCRYPT
|CRYPT_DECRYPT
,
1257 ivptr
, ivec_usage
) != 0)
1258 fatal(f
, "Cannot initialize encryption -"
1262 if (session_key
->enctype
== ENCTYPE_ARCFOUR_HMAC
||
1263 session_key
->enctype
== ENCTYPE_ARCFOUR_HMAC_EXP
) {
1264 if (configure_stream(f
, session_key
,
1265 CRYPT_ENCRYPT
|CRYPT_DECRYPT
,
1266 NULL
, IVEC_NEVER
) != 0)
1268 "Cannot initialize encryption -"
1272 if (krb5_c_block_size(krb_context
,
1273 session_key
->enctype
,
1275 syslog(LOG_ERR
, "Cannot determine blocksize "
1276 "for encryption type %d",
1277 session_key
->enctype
);
1278 fatal(f
, "Cannot determine blocksize "
1279 "for encryption - exiting.\n");
1281 ivec
.data
= (char *)malloc(blocksize
);
1282 ivec
.length
= blocksize
;
1283 if (ivec
.data
== NULL
)
1284 fatal(f
, "memory error - exiting\n");
1286 * Following MIT convention -
1287 * encrypt IV = 0x01 x blocksize
1288 * decrypt IV = 0x00 x blocksize
1289 * ivec_usage = IVEC_ONETIME
1291 * configure_stream separately for encrypt and
1292 * decrypt because there are 2 different IVs.
1294 * AES uses 0's for IV.
1296 if (session_key
->enctype
==
1297 ENCTYPE_AES128_CTS_HMAC_SHA1_96
||
1298 session_key
->enctype
==
1299 ENCTYPE_AES256_CTS_HMAC_SHA1_96
)
1300 (void) memset(ivec
.data
, 0x00, blocksize
);
1302 (void) memset(ivec
.data
, 0x01, blocksize
);
1303 if (configure_stream(f
, session_key
, CRYPT_ENCRYPT
,
1304 &ivec
, IVEC_ONETIME
) != 0)
1305 fatal(f
, "Cannot initialize encryption -"
1307 (void) memset(ivec
.data
, 0x00, blocksize
);
1308 if (configure_stream(f
, session_key
, CRYPT_DECRYPT
,
1309 &ivec
, IVEC_ONETIME
) != 0)
1310 fatal(f
, "Cannot initialize encryption -"
1313 (void) free(ivec
.data
);
1316 start_stream(f
, CRYPT_ENCRYPT
);
1317 start_stream(f
, CRYPT_DECRYPT
);
1319 protocol(f
, p
, encr_flag
);
1324 fatalperror(f
, "address conversion");
1329 * rlogin "protocol" machine.
1332 protocol(int f
, int p
, int encr_flag
)
1335 struct protocol_arg rloginp
;
1336 struct strioctl rloginmod
;
1337 int ptmfd
; /* fd of logindmux coneected to ptmx */
1338 int netfd
; /* fd of logindmux connected to netf */
1339 static uchar_t oobdata
[] = {TIOCPKT_WINDOW
};
1341 /* indicate new rlogin */
1342 if (send_oob(f
, oobdata
, 1) < 0)
1343 fatalperror(f
, "send_oob");
1345 * We cannot send the SECURE_MSG until after the
1346 * client has been signaled with the oobdata (above).
1349 if (write(f
, SECURE_MSG
, strlen(SECURE_MSG
)) < 0)
1350 fatalperror(f
, "Error writing SECURE message");
1354 * Open logindmux driver and link netf and ptmx
1355 * underneath logindmux.
1357 if ((ptmfd
= open("/dev/logindmux", O_RDWR
)) == -1)
1358 fatalperror(f
, "open /dev/logindmux");
1360 if ((netfd
= open("/dev/logindmux", O_RDWR
)) == -1)
1361 fatalperror(f
, "open /dev/logindmux");
1363 if (ioctl(ptmfd
, I_LINK
, p
) < 0)
1364 fatal(f
, "ioctl I_LINK of /dev/ptmx failed\n");
1366 if (ioctl(netfd
, I_LINK
, f
) < 0)
1367 fatal(f
, "ioctl I_LINK of tcp connection failed\n");
1370 * Figure out the device number of the ptm's mux fd, and pass that
1373 if (fstat(ptmfd
, &buf
) < 0)
1374 fatalperror(f
, "cannot determine device number"
1375 " of pty side of /dev/logindmux");
1376 rloginp
.dev
= buf
.st_rdev
;
1379 rloginmod
.ic_cmd
= LOGDMX_IOC_QEXCHANGE
;
1380 rloginmod
.ic_timout
= -1;
1381 rloginmod
.ic_len
= sizeof (struct protocol_arg
);
1382 rloginmod
.ic_dp
= (char *)&rloginp
;
1384 if (ioctl(netfd
, I_STR
, &rloginmod
) < 0)
1385 fatal(netfd
, "ioctl LOGDMX_IOC_QEXCHANGE of netfd failed\n");
1388 * Figure out the device number of the net's mux fd, and pass that
1391 if (fstat(netfd
, &buf
))
1392 fatalperror(f
, "cannot determine device number"
1393 " of network side of /dev/logindmux");
1394 rloginp
.dev
= buf
.st_rdev
;
1397 rloginmod
.ic_cmd
= LOGDMX_IOC_QEXCHANGE
;
1398 rloginmod
.ic_timout
= -1;
1399 rloginmod
.ic_len
= sizeof (struct protocol_arg
);
1400 rloginmod
.ic_dp
= (char *)&rloginp
;
1402 if (ioctl(ptmfd
, I_STR
, &rloginmod
) < 0)
1403 fatal(netfd
, "ioctl LOGDMXZ_IOC_QEXCHANGE of ptmfd failed\n");
1405 * Send an ioctl type RL_IOC_ENABLE to reenable the
1406 * message queue and reinsert the data read from streamhead
1407 * at the time of pushing rloginmod module.
1408 * We need to send this ioctl even if no data was read earlier
1409 * since we need to reenable the message queue of rloginmod module.
1411 rloginmod
.ic_cmd
= RL_IOC_ENABLE
;
1412 rloginmod
.ic_timout
= -1;
1414 rloginmod
.ic_len
= nsize
;
1415 rloginmod
.ic_dp
= rlbuf
;
1417 rloginmod
.ic_len
= 0;
1418 rloginmod
.ic_dp
= NULL
;
1421 if (ioctl(netfd
, I_STR
, &rloginmod
) < 0)
1422 fatal(netfd
, "ioctl RL_IOC_ENABLE of netfd failed\n");
1425 * User level daemon now pauses till the shell exits.
1430 /* This is a signal handler, hence the dummy argument */
1441 * TPI style replacement for socket send() primitive, so we don't require
1442 * sockmod to be on the stream.
1445 send_oob(int fd
, void *ptr
, size_t count
)
1447 struct T_exdata_req exd_req
;
1448 struct strbuf hdr
, dat
;
1451 exd_req
.PRIM_type
= T_EXDATA_REQ
;
1452 exd_req
.MORE_flag
= 0;
1454 hdr
.buf
= (char *)&exd_req
;
1455 hdr
.len
= sizeof (exd_req
);
1460 ret
= putmsg(fd
, &hdr
, &dat
, 0);
1467 fatal(int fd
, const char *msg
)
1470 size_t len
= strlen(msg
) + 16; /* enough for our wrapper */
1473 /* ASCII 001 is the error indicator */
1474 len
= snprintf(bufp
, len
, "\01rlogind: %s.\r\n", msg
);
1475 (void) write(fd
, bufp
, len
);
1482 fatalperror(int fd
, const char *msg
)
1486 int save_errno
= errno
;
1487 size_t len
= strlen(msg
);
1489 if ((errstr
= strerror(save_errno
))) {
1490 len
+= strlen(errstr
) + 3; /* 3 for ": " and \0 below */
1492 (void) snprintf(bufp
, len
, "%s: %s", msg
, errstr
);
1494 const char fmt
[] = "%s: Error %d";
1496 /* -4 for %s & %d. "*8/3" is bytes->decimal, pessimistically */
1497 len
+= sizeof (fmt
) -4 + (sizeof (save_errno
) *8 /3);
1499 (void) snprintf(bufp
, len
, fmt
, msg
, save_errno
);
1510 char user
[sizeof (up
->ut_user
) + 1];
1511 char ttyn
[sizeof (up
->ut_line
) + 1];
1512 char rhost
[sizeof (up
->ut_host
) + 1];
1514 /* while cleaning up dont allow disruption */
1515 (void) sigset(SIGCHLD
, SIG_IGN
);
1518 while (up
= getutxent()) {
1519 if (up
->ut_pid
== pid
) {
1520 if (up
->ut_type
== DEAD_PROCESS
)
1521 break; /* Cleaned up elsewhere. */
1524 * call pam_close_session if login changed
1525 * the utmpx user entry from type LOGIN_PROCESS
1526 * to type USER_PROCESS, which happens
1527 * after pam_open_session is called.
1529 if (up
->ut_type
== USER_PROCESS
) {
1530 (void) strlcpy(user
, up
->ut_user
,
1532 (void) strlcpy(ttyn
, up
->ut_line
,
1534 (void) strlcpy(rhost
, up
->ut_host
,
1538 * Use the same pam_prog_name that
1541 if ((pam_start(pam_prog_name
, user
, NULL
,
1544 (void) pam_set_item(pamh
, PAM_TTY
,
1546 (void) pam_set_item(pamh
, PAM_RHOST
,
1548 (void) pam_close_session(pamh
, 0);
1549 (void) pam_end(pamh
, PAM_SUCCESS
);
1553 up
->ut_type
= DEAD_PROCESS
;
1554 up
->ut_exit
.e_termination
= WTERMSIG(0);
1555 up
->ut_exit
.e_exit
= WEXITSTATUS(0);
1556 (void) time(&up
->ut_tv
.tv_sec
);
1558 if (modutx(up
) == NULL
) {
1560 * Since modutx failed we'll
1561 * write out the new entry
1564 (void) pututxline(up
);
1565 updwtmpx("wtmpx", up
);
1573 (void) sigset(SIGCHLD
, cleanup
);
1577 readstream(int fd
, char *buf
, int size
)
1579 struct strbuf ctlbuf
, datbuf
;
1580 union T_primitives tpi
;
1587 (void) memset(&ctlbuf
, 0, sizeof (ctlbuf
));
1588 (void) memset(&datbuf
, 0, sizeof (datbuf
));
1590 ctlbuf
.buf
= (char *)&tpi
;
1591 ctlbuf
.maxlen
= sizeof (tpi
);
1593 datbuf
.maxlen
= size
;
1596 if (ioctl(fd
, I_NREAD
, &nread
) < 0) {
1597 syslog(LOG_ERR
, "I_NREAD returned error %m");
1600 if (nread
+ nbytes
> bufsize
) {
1601 buf
= (char *)realloc(buf
, (unsigned)(bufsize
+ nread
));
1604 "buffer allocation failed\n");
1609 datbuf
.buf
= buf
+ nbytes
;
1611 datbuf
.maxlen
= bufsize
- nbytes
;
1612 ret
= getmsg(fd
, &ctlbuf
, &datbuf
, &flags
);
1614 syslog(LOG_ERR
, "getmsg failed error %m");
1617 if ((ctlbuf
.len
== 0) && (datbuf
.len
== 0)) {
1619 * getmsg() returned no data - this indicates
1620 * that the connection is closing down.
1624 if (ctlbuf
.len
<= 0) {
1625 nbytes
+= datbuf
.len
;
1626 datbuf
.buf
+= datbuf
.len
;
1629 if (tpi
.type
== T_DATA_REQ
) {
1632 if ((tpi
.type
== T_ORDREL_IND
) || (tpi
.type
== T_DISCON_IND
))
1638 * Verify that the named module is at the top of the stream
1639 * and then pop it off.
1642 removemod(int f
, char *modname
)
1644 char topmodname
[BUFSIZ
];
1646 if (ioctl(f
, I_LOOK
, topmodname
) < 0)
1648 if (strcmp(modname
, topmodname
) != 0) {
1652 if (ioctl(f
, I_POP
, 0) < 0)