Search

Monday, November 14, 2016

OpenVMS Log Files Remote Logging to Unix/Linux SYSLOG Facility RSYSLOG

OpenVMS 7.3 on VAX logs on a remote Linux RSYSLOG syslog server
I gather all the logs I can from my many hobbyist servers and systems in a central place on one of my Virtual Private Servers running a rsyslog logger daemon on CentOS 7.

I wanted to add my QCOCAL hobbyist OpenVMS 7.3 VAX system logs to this central syslog server on my VPS.

Fortunately, all the work has already been done by Doug O'Neal from Homewood Academic Computing at Johns Hopkins University, whose SYSLOGD.C program pretty much worked out of the box.

The only tweak I made to Doug's SYSLOGD.C is to support a second OpenVMS Logical "SYSLOGD_PORT" to specify the UDP port number of my remote RSYSLOG Linux server, since I am not running it on the standard port (514). This is in addition to the "SYSLOGD_SERVER" Logical already supported by Doug's code.

Both Logicals need to be defined in the SYSTEM Logical Table LNM$SYSTEM_TABLE. Something like the following three lines in the OpenVMS startup file (SYS$MANAGER:SYSTARTUP_VMS.COM for my OpenVMS 7.3 VAX installation) suffice to start the VMS SYSLOG client at boot time;

$ define/system syslogd_server "64.137.248.212" ! IP address sanyalnet-cloud-vps.freeddns.org
$ define/system syslogd_port "65514"            ! UDP Port that remote syslogd is listening on
$ run/detached/process_name=syslogd/input=nl:/output=nl:/error=nl: DUA0:[TOOLS.SYSLOGD]SYSLOGD.EXE

Below is the SYSLOGD.C source code originally by Doug with the minor modifications by me. All credit to Doug O'Neal. This code compiles fine on my installation of Compaq C V6.4-005 on OpenVMS VAX V7.3 and I suspect it will on other versions, including OpenVMS ALPHA per Doug's comments at the top of the code. To build a binary,

$ CC  SYSLOGD
$ LINK  SYSLOGD

Some warning and informationals are generated by the compiler and linker, but nothing to stop SYSLOGD.EXE from being generated and used.

You can also download the source and OpenVMS VAX V7.3 binary executable from my FAL area on QCOCAL over HECNET or over the internet from QCOCAL served by WASD.

Making it secure


The OpenVMS SYSLOGD facility sends log content to the remote RSYSLOG server with no encryption, in cleartext over UDP. This is totally insecure - any rookie hacker could sniff the packets and learn a lot about what is going on with our OpenVMS server.

We need to make the transmission of the logs to the remote server secure.

Fortunately, I have recently setup a secure tunnel for logging to my remote central log server from my other hobbyist servers, as described in this post. This makes it really easy to secure OpenVMS log transmissions to the central server.

First, I identified a syslog server on the local LAN that is already configured as a secure tunnel (stunnel) client to my central log server. I decided to use the same Linux host (10.42.2.2) that is running the SIMH OpenVMS VAX for this purpose. It is directly and quickly accessible from the SIMH VAX (10.42.2.5) because the SIMH VAX network link is just a tun/tap bridge on the same server.

I opened up /etc/rsyslog.conf on this SIMH host Linux server, and uncommented the following lines to allow rsyslog to accept logger connections over network.

# Provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514

# Provides TCP syslog reception
$ModLoad imtcp
$InputTCPServerRun 514

The TCP syslog reception port did not really need to be opened up for this rsyslog daemon to accept log entries over UDP from SYSLOGD.EXE on OpenVMS VAX, but may come in useful for later projects.

A restart of the rsyslog daemon on the Linux host is required after editing /etc/rsyslog.conf:

# service rsyslog restart

Then I logged into the OpenVMS VAX server and modified the OpenVMS SYSLOGD startup commands to point to the Linux SIMH host instead of the remote syslog server:

$ define/system syslogd_server "10.42.2.2"      ! IP address of the host this SIMH VAX is running on
$ define/system syslogd_port "514"              ! UDP Port of the host rsyslog server

SYSLOG then needs to be restarted on OpenVMS VAX:

$ show system
$ stop/id=<id of SYSLOGD>
$ run/detached/process_name=syslogd2vps/input=nl:/output=nl:/error=nl: DUA0:[TOOLS.SYSLOGD]SYSLOGD.EXE

Going back to the Linux host, an examination of the logs now reveals the VAX Server is sending logs here:

OpenVMS VAX SYSLOGD,EXE logging to rsyslog logger daemon running on it's own SIMH VAX linus host

And here is the fun part. Since this rsyslog daemon is already configured to use stunnel to send logs securely to the remote server, the logs from OpenVMS VAX are also forwarded to the remote syslogd daemon over the same secure tunnel.

Sure enough, checking my remote VPS central syslog server log, I see the OpenVMS VAX logs dutifully forwarded by the local SIMH host Linux box.

syslog entries on central rsyslog linux server from OpenVMS VAX forwarded by SIMH VAX Linux host over stunnel secure tunnel



/*
* syslogd.c
* Take OPCOM messages received over a set of pseduo-ttys and redirect
* them to a UNIX syslogd. Tested with Multinet and UCX software under
* VMS 1.5 & 6.1 (AXP) and VMS 5.5 & 6.0 (VAX).
*
* The chan structure defines the translation of the opcom classes to
* Unix facilities/priorities. The Unix server is defined via the
* logical name SYSLOGD_SERVER in the LNM$SYSTEM_TABLE logical name table.
* Either a host name or an IP address may be used.
*
* must be linked against either
* multinet:multinet_socket_library/share
* or
* sys$share:ucx$ipc_shr/share
*
* On an AXP system with UCX 3.1, /STANDARD=VAXC must be specified for CC.
*
* Doug O'Neal
* Homewood Academic Computing
* Johns Hopkins University
* doug@jhuvms.hcf.jhu.edu
*
* Modified by Supratim Sanyal (supratim at riseup.net):
* - 14-NOV-2016: Added logical name SYSLOGD_PORT to specify the port number of
* remote syslog server - required in addition to SYSLOGD_SERVER
* in the LNM$SYSTEM_TABLE logical name table.
* - 01-NOV-2017: Worked around ftname[retlen-1] which was crashing in EXE by
* HP C V7.3-009 on OpenVMS Alpha V8.3
* http://supratim-sanyal.blogspot.com/2016/11/openvms-log-files-remote-logging-to.html
*/
#define UCX 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <ssdef.h>
#include <dvidef.h>
#include <descrip.h>
#include <stsdef.h>
#include <lnmdef.h>
#include <iodef.h>
#include <opcdef.h>
#ifdef MULTINET
#include "multinet_root:[multinet.include.sys]types.h"
#include "multinet_root:[multinet.include.sys]socket.h"
#include "multinet_root:[multinet.include.netinet]in.h"
#include "multinet_root:[multinet.include]netdb.h"
#endif
#ifdef UCX
#include <types.h>
#include <socket.h>
#include <in.h>
#include <netdb.h>
#include <ucx$inetdef.h>
#endif
unsigned int PTD$CREATE();
unsigned int PTD$READ();
unsigned int PTD$READW();
unsigned int SYS$QIOW();
unsigned int SYS$EXPREG();
unsigned int SYS$GETDVIW();
unsigned int SYS$SNDOPR();
unsigned int SYS$TRNLNM();
unsigned int write_syslog();
int sockfd;
struct sockaddr_in cli_addr, serv_addr;
#define FT_DATA_MAX (unsigned short) 508
struct ft_buffer {
unsigned short status, count;
unsigned char data[FT_DATA_MAX];
};
struct iosb {
unsigned short condition, transfer_count;
unsigned long info;
};
// #define SERV_UDP_PORT 514
// #define SERV_UDP_PORT 65514
int SERV_UDP_PORT=514;
/*
* Facility codes
*/
#define LOG_KERN (0<<3) /* kernel messages */
#define LOG_USER (1<<3) /* random user-level messages */
#define LOG_MAIL (2<<3) /* mail system */
#define LOG_DAEMON (3<<3) /* system daemons */
#define LOG_AUTH (4<<3) /* security/authorization messages */
#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
#define LOG_LPR (6<<3) /* line printer subsystem */
#define LOG_NEWS (7<<3) /* netnews subsystem */
#define LOG_UUCP (8<<3) /* uucp subsystem */
#define LOG_CRON (15<<3) /* cron/at subsystem */
#define LOG_LOCAL0 (16<<3) /* reserved for local use */
#define LOG_LOCAL1 (17<<3) /* reserved for local use */
#define LOG_LOCAL2 (18<<3) /* reserved for local use */
#define LOG_LOCAL3 (19<<3) /* reserved for local use */
#define LOG_LOCAL4 (20<<3) /* reserved for local use */
#define LOG_LOCAL5 (21<<3) /* reserved for local use */
#define LOG_LOCAL6 (22<<3) /* reserved for local use */
#define LOG_LOCAL7 (23<<3) /* reserved for local use */
/*
* Priorities (these are ordered)
*/
#define LOG_EMERG 0 /* system is unusable */
#define LOG_ALERT 1 /* action must be taken immediately */
#define LOG_CRIT 2 /* critical conditions */
#define LOG_ERR 3 /* error conditions */
#define LOG_WARNING 4 /* warning conditions */
#define LOG_NOTICE 5 /* normal but signification condition */
#define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */
struct {
unsigned short chan;
unsigned int class, priority;
struct ft_buffer *buffer;
} chan[] =
{ {0, OPC$M_NM_CENTRL|OPC$M_NM_REPLY, LOG_USER|LOG_NOTICE, 0},
{0, OPC$M_NM_PRINT|OPC$M_NM_CARDS, LOG_LPR|LOG_INFO, 0},
{0, OPC$M_NM_TAPES|OPC$M_NM_DISKS|OPC$M_NM_DEVICE, LOG_KERN|LOG_ERR, 0},
{0, OPC$M_NM_NTWORK, LOG_DAEMON|LOG_INFO, 0},
{0, OPC$M_NM_CLUSTER, LOG_KERN|LOG_CRIT, 0},
{0, OPC$M_NM_SECURITY, LOG_AUTH|LOG_WARNING, 0},
{0, OPC$M_NM_SOFTWARE|OPC$M_NM_LICENSE, LOG_KERN|LOG_WARNING, 0},
{0, OPC$M_NM_OPER1|OPC$M_NM_OPER2|OPC$M_NM_OPER3|OPC$M_NM_OPER4|
OPC$M_NM_OPER5|OPC$M_NM_OPER6|OPC$M_NM_OPER7|OPC$M_NM_OPER8|
OPC$M_NM_OPER9|OPC$M_NM_OPER10|OPC$M_NM_OPER11|OPC$M_NM_OPER12,
LOG_LOCAL0|LOG_INFO, 0}
};
#ifdef UCX
void bzero(c,length)
char *c; int length;
{
char *c1;
for (c1=c; length--; ) *c1++=0;
}
void bcopy(c1, c2, length)
char *c1, *c2; int length;
{
if (length==0 || c1==c2) return;
if (c1 > c2) {
for (;length--;) *c2++ = *c1++;
} else {
c1 += length;
c2 += length;
for (;length--;) *--c2 = *--c1;
}
}
#endif
main(argc, argv)
int argc; char *argv[];
{
unsigned int status, acmode, charbuff, buflen, astadr, astprm;
unsigned int ast_acmode, inadr[2], retlen;
unsigned short i;
unsigned int nchan, ichan;
struct {
unsigned short length, code;
unsigned long bufaddr, retlenaddr;
unsigned long zero;
} itemlist;
struct {
unsigned char class, type;
unsigned short width;
unsigned char basic[3], length;
unsigned int extended;
} term_char;
struct {
unsigned char type, enab[3];
unsigned int mask;
unsigned short ounit;
unsigned char str_length, string[16];
} msgbuf;
char ftname[64];
struct dsc$descriptor_s enable;
struct iosb iosb;
$DESCRIPTOR(tabnam,"LNM$SYSTEM_TABLE");
$DESCRIPTOR(syslog_host,"SYSLOGD_SERVER");
$DESCRIPTOR(syslog_port,"SYSLOGD_PORT");
char serv_host_name[255];
char serv_port[255];
struct hostent *serv_host_addr;
nchan = sizeof chan / sizeof chan[0];
memset(ftname,0,sizeof(ftname));
/*
* Set up the UDP connection to the syslog server
*/
/*
* Fill in the structure "serv_addr" with the address of the
* server that we want to send to.
*/
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
/*
* first, check that we can get the IP address of the syslogd server
*/
itemlist.length = sizeof(serv_host_name);
itemlist.code = LNM$_STRING;
itemlist.bufaddr = serv_host_name;
itemlist.retlenaddr = &retlen;
itemlist.zero = 0;
status = SYS$TRNLNM(0,&tabnam,&syslog_host,0,&itemlist);
if (!($VMS_STATUS_SUCCESS(status))) {
fprintf(stderr,"Cannot translate logical name %s in table %s\n",syslog_host.dsc$a_pointer,tabnam.dsc$a_pointer);
exit (status);
}
serv_host_addr=gethostbyname(serv_host_name);
if (!serv_host_addr) {
serv_addr.sin_addr.s_addr = inet_addr(serv_host_name);
if (serv_addr.sin_addr.s_addr == -1) {
fprintf(stderr,"Cannot resolve name %s\n",serv_host_name);
exit (1);
}
} else {
bcopy(serv_host_addr->h_addr, (char *) &serv_addr.sin_addr,
serv_host_addr->h_length);
}
/*
* then, check that we can get the port number of the syslogd server
*/
itemlist.length = sizeof(serv_port);
itemlist.code = LNM$_STRING;
itemlist.bufaddr = serv_port;
itemlist.retlenaddr = &retlen;
itemlist.zero = 0;
status = SYS$TRNLNM(0,&tabnam,&syslog_port,0,&itemlist);
if (!($VMS_STATUS_SUCCESS(status))) {
fprintf(stderr,"Cannot translate logical name %s in table %s\n",syslog_port.dsc$a_pointer,tabnam.dsc$a_pointer);
exit (status);
}
SERV_UDP_PORT=atoi(serv_port);
if(0==SERV_UDP_PORT) {
fprintf(stderr,"Cannot translate port number from logical %s\n",syslog_host.dsc$a_pointer);
exit (1);
}
serv_addr.sin_port = htons(SERV_UDP_PORT);
/*
* Open a UDP socket (an Internet datagram socket).
*/
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("client: can't open datagram socket\n");
exit (1);
}
/*
* Bind any local address for us.
*/
bzero((char *) &cli_addr, sizeof(cli_addr)); /* zero out */
cli_addr.sin_family = AF_INET;
cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
cli_addr.sin_port = htons(0);
if (bind(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) {
perror("client: can't bind local address\n");
exit (1) ;
}
/*
* set up a pseudoterminal device for each operator class defined
* and enable that device through opcom
*/
for (ichan=0; ichan<nchan; ichan++) {
/* set aside memory for the pty device */
acmode = 0;
charbuff = 0;
buflen = 0;
astadr = 0;
astprm = 0;
ast_acmode = 0;
status = SYS$EXPREG(1,inadr,0,0);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
/* create the pty device */
chan[ichan].buffer = inadr[0];
status = PTD$CREATE(&chan[ichan].chan, acmode, charbuff, buflen, astadr,
astprm, ast_acmode, inadr);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
/* set the pty device to maximum page width, infinite page length */
status = SYS$QIOW(0, chan[ichan].chan, IO$_SENSECHAR, &iosb, 0, 0,
&term_char, sizeof(term_char), 0, 0, 0, 0);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
term_char.width=511;
term_char.length=0;
status = SYS$QIOW(0, chan[ichan].chan, IO$_SETCHAR, &iosb, 0, 0,
&term_char, sizeof(term_char), 0, 0, 0, 0);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
/* get the pty device name */
itemlist.length = sizeof(ftname);
itemlist.code = DVI$_FULLDEVNAM;
itemlist.bufaddr = ftname;
itemlist.retlenaddr = &retlen;
itemlist.zero = 0;
status = SYS$GETDVIW(0,chan[ichan].chan,0,&itemlist,0,0,0,0);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
/* ftname[retlen-1]='\0'; -- This crashes on OpenVMS 8.3 Alpha, memsetting this at declaration above - Supratim Sanyal 01-NOV-201
7 */
msgbuf.type = OPC$_RQ_TERME;
msgbuf.enab[0] = 1;
msgbuf.mask = chan[ichan].class;
for (i=retlen-2; i>0; i--) {
if ((ftname[i] >= 'A') && (ftname[i] <= 'Z')) {
msgbuf.ounit = (short) atoi(&ftname[i+1]);
ftname[i+1] = '\0';
break;
}
}
msgbuf.str_length = strlen(ftname);
strncpy(msgbuf.string,ftname,msgbuf.str_length);
enable.dsc$w_length = sizeof(msgbuf);
enable.dsc$b_dtype = DSC$K_DTYPE_T;
enable.dsc$b_class = DSC$K_CLASS_S;
enable.dsc$a_pointer = &msgbuf;
status = SYS$SNDOPR(&enable,0);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
}
/*
* Now set up async reads to each channel. As various opcom messages come
* in, the appropriate ast will fire and the message logged. Another read
* will then be queued.
*/
for (ichan=0; ichan<nchan; ichan++) {
status = PTD$READ(0, chan[ichan].chan, &write_syslog, ichan,
chan[ichan].buffer, FT_DATA_MAX);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
}
status = SYS$HIBER();
exit(status);
}
unsigned int write_syslog(ichan)
unsigned int ichan;
{
unsigned int i;
char header[1024], buffer[512], *c1;
time_t now;
(void) SYS$SETAST(0); /* block new ast's */
time(&now);
c1 = buffer;
for (i=0; i<=chan[ichan].buffer->count; i++) {
if (chan[ichan].buffer->data[i]=='\015') { /* CR delimited */
*c1 = '\0';
sprintf(header,"<%d>%.15s [OPCOM] ", chan[ichan].priority,
(ctime(&now)+4));
strcat(header,buffer);
sendto(sockfd, header, strlen(header), 0, &serv_addr,
sizeof serv_addr);
c1 = buffer;
} else if (isprint(chan[ichan].buffer->data[i])) {
*c1 = chan[ichan].buffer->data[i];
c1++;
}
}
/* queue a new read */
(void) PTD$READ(0, chan[ichan].chan, &write_syslog, ichan,
chan[ichan].buffer, FT_DATA_MAX);
(void) SYS$SETAST(1); /* re-enable ast's */
return(0);
}
view raw SYSLOGD.C hosted with ❤ by GitHub

No comments:

Post a Comment

"SEO" link builders: move on, your spam link will not get posted.

Note: Only a member of this blog may post a comment.

Recommended Products from Amazon