summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/ntpd/ntp_control.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/ntpd/ntp_control.c')
-rw-r--r--contrib/ntp/ntpd/ntp_control.c315
1 files changed, 168 insertions, 147 deletions
diff --git a/contrib/ntp/ntpd/ntp_control.c b/contrib/ntp/ntpd/ntp_control.c
index d7109ca..0ac0404 100644
--- a/contrib/ntp/ntpd/ntp_control.c
+++ b/contrib/ntp/ntpd/ntp_control.c
@@ -18,10 +18,6 @@
#include <netinet/in.h>
#include <arpa/inet.h>
-#ifdef PUBKEY
-#include "ntp_crypto.h"
-#endif /* PUBKEY */
-
/*
* Structure to hold request procedure information
*/
@@ -46,7 +42,9 @@ struct ctl_proc {
* Request processing routines
*/
static void ctl_error P((int));
+#ifdef REFCLOCK
static u_short ctlclkstatus P((struct refclockstat *));
+#endif
static void ctl_flushpkt P((int));
static void ctl_putdata P((const char *, unsigned int, int));
static void ctl_putstr P((const char *, const char *,
@@ -56,7 +54,7 @@ static void ctl_putuint P((const char *, u_long));
static void ctl_puthex P((const char *, u_long));
static void ctl_putint P((const char *, long));
static void ctl_putts P((const char *, l_fp *));
-static void ctl_putadr P((const char *, u_int32));
+static void ctl_putadr P((const char *, u_int32, struct sockaddr_storage*));
static void ctl_putid P((const char *, char *));
static void ctl_putarray P((const char *, double *, int));
static void ctl_putsys P((int));
@@ -74,7 +72,7 @@ static void read_clock_status P((struct recvbuf *, int));
static void write_clock_status P((struct recvbuf *, int));
static void set_trap P((struct recvbuf *, int));
static void unset_trap P((struct recvbuf *, int));
-static struct ctl_trap *ctlfindtrap P((struct sockaddr_in *,
+static struct ctl_trap *ctlfindtrap P((struct sockaddr_storage *,
struct interface *));
static struct ctl_proc control_codes[] = {
@@ -114,16 +112,16 @@ static struct ctl_var sys_var[] = {
{ CS_VERSION, RO, "version" }, /* 17 */
{ CS_STABIL, RO, "stability" }, /* 18 */
{ CS_VARLIST, RO, "sys_var_list" }, /* 19 */
-#ifdef PUBKEY
+#ifdef OPENSSL
{ CS_FLAGS, RO, "flags" }, /* 20 */
{ CS_HOST, RO, "hostname" }, /* 21 */
- { CS_PUBLIC, RO, "publickey" }, /* 22 */
- { CS_CERTIF, RO, "certificate" }, /* 23 */
- { CS_DHPARAMS, RO, "params" }, /* 24 */
- { CS_REVTIME, RO, "refresh" }, /* 25 */
- { CS_LEAPTAB, RO, "leapseconds" }, /* 26 */
- { CS_TAI, RO, "tai"}, /* 27 */
-#endif /* PUBKEY */
+ { CS_PUBLIC, RO, "hostkey" }, /* 22 */
+ { CS_CERTIF, RO, "cert" }, /* 23 */
+ { CS_REVTIME, RO, "refresh" }, /* 24 */
+ { CS_LEAPTAB, RO, "leapseconds" }, /* 25 */
+ { CS_TAI, RO, "tai" }, /* 26 */
+ { CS_DIGEST, RO, "signature" }, /* 27 */
+#endif /* OPENSSL */
{ 0, EOV, "" } /* 28 */
};
@@ -152,14 +150,15 @@ static u_char def_sys_var[] = {
CS_DRIFT,
CS_JITTER,
CS_STABIL,
-#ifdef PUBKEY
- CS_FLAGS,
+#ifdef OPENSSL
CS_HOST,
- CS_CERTIF,
- CS_DHPARAMS,
+ CS_DIGEST,
+ CS_FLAGS,
+ CS_PUBLIC,
CS_REVTIME,
CS_LEAPTAB,
-#endif /* PUBKEY */
+ CS_CERTIF,
+#endif /* OPENSSL */
0
};
@@ -205,20 +204,18 @@ static struct ctl_var peer_var[] = {
{ CP_FILTERROR, RO, "filtdisp=" }, /* 34 */
{ CP_FLASH, RO, "flash" }, /* 35 */
{ CP_TTL, RO, "ttl" }, /* 36 */
- { CP_TTLMAX, RO, "ttlmax" }, /* 37 */
+ { CP_RANK, RO, "rank" }, /* 37 */
{ CP_VARLIST, RO, "peer_var_list" }, /* 38 */
-#ifdef PUBKEY
- { CP_FLAGS, RO, "flags" }, /* 38 */
- { CP_HOST, RO, "hostname" }, /* 39 */
- { CP_PUBLIC, RO, "publickey" }, /* 40 */
- { CP_CERTIF, RO, "certificate" }, /* 41 */
- { CP_SESKEY, RO, "pcookie" }, /* 42 */
- { CP_SASKEY, RO, "hcookie" }, /* 43 */
- { CP_INITSEQ, RO, "initsequence" }, /* 44 */
- { CP_INITKEY, RO, "initkey" }, /* 45 */
- { CP_INITTSP, RO, "timestamp" }, /* 46 */
-#endif /* PUBKEY */
- { 0, EOV, "" } /* 47 */
+#ifdef OPENSSL
+ { CP_FLAGS, RO, "flags" }, /* 39 */
+ { CP_HOST, RO, "hostname" }, /* 40 */
+ { CP_INITSEQ, RO, "initsequence" }, /* 41 */
+ { CP_INITKEY, RO, "initkey" }, /* 42 */
+ { CP_INITTSP, RO, "timestamp" }, /* 43 */
+ { CP_DIGEST, RO, "signature" }, /* 44 */
+ { CP_IDENT, RO, "identity" }, /* 45 */
+#endif /* OPENSSL */
+ { 0, EOV, "" } /* 39/46 */
};
@@ -245,7 +242,6 @@ static u_char def_peer_var[] = {
CP_FLASH,
CP_KEYID,
CP_TTL,
- CP_TTLMAX,
CP_OFFSET,
CP_DELAY,
CP_DISPERSION,
@@ -257,13 +253,13 @@ static u_char def_peer_var[] = {
CP_FILTDELAY,
CP_FILTOFFSET,
CP_FILTERROR,
-#ifdef PUBKEY
- CP_FLAGS,
+#ifdef OPENSSL
CP_HOST,
- CP_CERTIF,
- CP_SESKEY,
+ CP_DIGEST,
+ CP_FLAGS,
+ CP_IDENT,
CP_INITSEQ,
-#endif /* PUBKEY */
+#endif /* OPENSSL */
0
};
@@ -450,7 +446,7 @@ static u_char * datapt;
static u_char * dataend;
static int datalinelen;
static int datanotbinflag;
-static struct sockaddr_in *rmt_addr;
+static struct sockaddr_storage *rmt_addr;
static struct interface *lcl_inter;
static u_char res_authenticate;
@@ -735,14 +731,16 @@ ctlpeerstatus(
/*
* ctlclkstatus - return a status word for this clock
*/
+#ifdef REFCLOCK
static u_short
ctlclkstatus(
struct refclockstat *this_clock
)
{
- return ((u_short)(this_clock->currentstatus) << 8) |
- (u_short)(this_clock->lastevent);
+ return ((u_short)(((this_clock->currentstatus) << 8) |
+ (this_clock->lastevent)));
}
+#endif
/*
@@ -1074,12 +1072,13 @@ ctl_putts(
/*
- * ctl_putadr - write a dotted quad IP address into the response
+ * ctl_putadr - write an IP address into the response
*/
static void
ctl_putadr(
const char *tag,
- u_int32 addr
+ u_int32 addr32,
+ struct sockaddr_storage* addr
)
{
register char *cp;
@@ -1092,7 +1091,10 @@ ctl_putadr(
*cp++ = *cq++;
*cp++ = '=';
- cq = numtoa(addr);
+ if (addr == NULL)
+ cq = numtoa(addr32);
+ else
+ cq = stoa(addr);
while (*cq != '\0')
*cp++ = *cq++;
ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
@@ -1139,7 +1141,6 @@ ctl_putarray(
register const char *cq;
char buffer[200];
int i;
-
cp = buffer;
cq = tag;
while (*cq != '\0')
@@ -1166,9 +1167,11 @@ ctl_putsys(
)
{
l_fp tmp;
-#ifdef HAVE_UNAME
char str[256];
-#endif
+#ifdef OPENSSL
+ struct cert_info *cp;
+ char cbuf[256];
+#endif /* OPENSSL */
switch (varid) {
@@ -1195,8 +1198,8 @@ ctl_putsys(
break;
case CS_REFID:
- if (sys_stratum > 1)
- ctl_putadr(sys_var[CS_REFID].text, sys_refid);
+ if (sys_stratum > 1 && sys_stratum < STRATUM_UNSPEC)
+ ctl_putadr(sys_var[CS_REFID].text, sys_refid, NULL);
else
ctl_putid(sys_var[CS_REFID].text,
(char *)&sys_refid);
@@ -1254,8 +1257,7 @@ ctl_putsys(
ctl_putstr(sys_var[CS_SYSTEM].text, str_system,
sizeof(str_system) - 1);
#else
- (void)strcpy(str, utsnamebuf.sysname);
- (void)strcat(str, utsnamebuf.release);
+ sprintf(str, "%s/%s", utsnamebuf.sysname, utsnamebuf.release);
ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str));
#endif /* HAVE_UNAME */
break;
@@ -1333,37 +1335,50 @@ ctl_putsys(
}
break;
-#ifdef PUBKEY
+#ifdef OPENSSL
case CS_FLAGS:
- if (crypto_flags)
- ctl_puthex(sys_var[CS_FLAGS].text,
- crypto_flags);
+ if (crypto_flags) {
+ ctl_puthex(sys_var[CS_FLAGS].text, crypto_flags);
+ }
+ break;
+
+ case CS_DIGEST:
+ if (crypto_flags) {
+ const EVP_MD *dp;
+
+ dp = EVP_get_digestbynid(crypto_flags >> 16);
+ strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp)));
+ ctl_putstr(sys_var[CS_DIGEST].text, str,
+ strlen(str));
+ }
break;
case CS_HOST:
- ctl_putstr(sys_var[CS_HOST].text, sys_hostname,
- strlen(sys_hostname));
- if (host.fstamp != 0)
- ctl_putuint(sys_var[CS_PUBLIC].text,
- ntohl(host.fstamp));
+ if (sys_hostname != NULL)
+ ctl_putstr(sys_var[CS_HOST].text, sys_hostname,
+ strlen(sys_hostname));
break;
case CS_CERTIF:
- if (certif.fstamp != 0)
- ctl_putuint(sys_var[CS_CERTIF].text,
- ntohl(certif.fstamp));
+ for (cp = cinfo; cp != NULL; cp = cp->link) {
+ sprintf(cbuf, "%s %s 0x%x %u", cp->subject,
+ cp->issuer, cp->flags,
+ ntohl(cp->cert.fstamp));
+ ctl_putstr(sys_var[CS_CERTIF].text, cbuf,
+ strlen(cbuf));
+ }
break;
- case CS_DHPARAMS:
- if (dhparam.fstamp != 0)
- ctl_putuint(sys_var[CS_DHPARAMS].text,
- ntohl(dhparam.fstamp));
+ case CS_PUBLIC:
+ if (hostval.fstamp != 0)
+ ctl_putuint(sys_var[CS_PUBLIC].text,
+ ntohl(hostval.fstamp));
break;
case CS_REVTIME:
- if (host.tstamp != 0)
+ if (hostval.tstamp != 0)
ctl_putuint(sys_var[CS_REVTIME].text,
- ntohl(host.tstamp));
+ ntohl(hostval.tstamp));
break;
case CS_LEAPTAB:
@@ -1373,7 +1388,7 @@ ctl_putsys(
if (sys_tai != 0)
ctl_putuint(sys_var[CS_TAI].text, sys_tai);
break;
-#endif /* PUBKEY */
+#endif /* OPENSSL */
}
}
@@ -1387,6 +1402,11 @@ ctl_putpeer(
struct peer *peer
)
{
+#ifdef OPENSSL
+ char str[256];
+ struct autokey *ap;
+#endif /* OPENSSL */
+
switch (varid) {
case CP_CONFIG:
@@ -1405,24 +1425,24 @@ ctl_putpeer(
break;
case CP_SRCADR:
- ctl_putadr(peer_var[CP_SRCADR].text,
- peer->srcadr.sin_addr.s_addr);
+ ctl_putadr(peer_var[CP_SRCADR].text, 0,
+ &peer->srcadr);
break;
case CP_SRCPORT:
ctl_putuint(peer_var[CP_SRCPORT].text,
- ntohs(peer->srcadr.sin_port));
+ ntohs(((struct sockaddr_in*)&peer->srcadr)->sin_port));
break;
case CP_DSTADR:
- ctl_putadr(peer_var[CP_DSTADR].text,
- peer->dstadr->sin.sin_addr.s_addr);
+ ctl_putadr(peer_var[CP_DSTADR].text, 0,
+ &(peer->dstadr->sin));
break;
case CP_DSTPORT:
ctl_putuint(peer_var[CP_DSTPORT].text,
(u_long)(peer->dstadr ?
- ntohs(peer->dstadr->sin.sin_port) : 0));
+ ntohs(((struct sockaddr_in*)&peer->dstadr->sin)->sin_port) : 0));
break;
case CP_LEAP:
@@ -1461,16 +1481,22 @@ ctl_putpeer(
break;
case CP_REFID:
- if (peer->stratum > 1) {
- if (peer->flags & FLAG_REFCLOCK)
- ctl_putadr(peer_var[CP_REFID].text,
- peer->srcadr.sin_addr.s_addr);
+ if (peer->flags & FLAG_REFCLOCK) {
+ if (peer->stratum > 0 && peer->stratum <
+ STRATUM_UNSPEC)
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->refid, NULL);
else
- ctl_putadr(peer_var[CP_REFID].text,
- peer->refid);
+ ctl_putid(peer_var[CP_REFID].text,
+ (char *)&peer->refid);
} else {
- ctl_putid(peer_var[CP_REFID].text,
- (char *)&peer->refid);
+ if (peer->stratum > 1 && peer->stratum <
+ STRATUM_UNSPEC)
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->refid, NULL);
+ else
+ ctl_putid(peer_var[CP_REFID].text,
+ (char *)&peer->refid);
}
break;
@@ -1499,21 +1525,17 @@ ctl_putpeer(
break;
case CP_TTL:
- if (!(peer->cast_flags & MDF_ACAST))
- break;
- ctl_putint(peer_var[CP_TTL].text, peer->ttl);
- break;
-
- case CP_TTLMAX:
- if (!(peer->cast_flags & (MDF_MCAST | MDF_ACAST)))
- break;
- ctl_putint(peer_var[CP_TTLMAX].text, peer->ttlmax);
+ ctl_putint(peer_var[CP_TTL].text, sys_ttl[peer->ttl]);
break;
case CP_VALID:
ctl_putuint(peer_var[CP_VALID].text, peer->unreach);
break;
+ case CP_RANK:
+ ctl_putuint(peer_var[CP_RANK].text, peer->rank);
+ break;
+
case CP_TIMER:
ctl_putuint(peer_var[CP_TIMER].text,
peer->nextdate - current_time);
@@ -1607,47 +1629,44 @@ ctl_putpeer(
ctl_putdata(buf, (unsigned)(s - buf), 0);
}
break;
-#ifdef PUBKEY
+#ifdef OPENSSL
case CP_FLAGS:
if (peer->crypto)
ctl_puthex(peer_var[CP_FLAGS].text, peer->crypto);
break;
- case CP_HOST:
- if (peer->keystr != NULL)
- ctl_putstr(peer_var[CP_HOST].text, peer->keystr,
- strlen(peer->keystr));
- if (peer->pubkey.fstamp != 0)
- ctl_putuint(peer_var[CP_PUBLIC].text,
- peer->pubkey.fstamp);
+ case CP_DIGEST:
+ if (peer->crypto) {
+ const EVP_MD *dp;
+
+ dp = EVP_get_digestbynid(peer->crypto >> 16);
+ strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp)));
+ ctl_putstr(peer_var[CP_DIGEST].text, str,
+ strlen(str));
+ }
break;
- case CP_CERTIF:
- if (peer->certif.fstamp != 0)
- ctl_putuint(peer_var[CP_CERTIF].text,
- peer->certif.fstamp);
+ case CP_HOST:
+ if (peer->subject != NULL)
+ ctl_putstr(peer_var[CP_HOST].text, peer->subject,
+ strlen(peer->subject));
break;
- case CP_SESKEY:
- if (peer->pcookie.key != 0)
- ctl_puthex(peer_var[CP_SESKEY].text,
- peer->pcookie.key);
- if (peer->hcookie != 0)
- ctl_puthex(peer_var[CP_SASKEY].text,
- peer->hcookie);
+ case CP_IDENT:
+ if (peer->issuer != NULL)
+ ctl_putstr(peer_var[CP_IDENT].text, peer->issuer,
+ strlen(peer->issuer));
break;
case CP_INITSEQ:
- if (peer->recauto.key == 0)
+ if ((ap = (struct autokey *)peer->recval.ptr) == NULL)
break;
- ctl_putint(peer_var[CP_INITSEQ].text,
- peer->recauto.seq);
- ctl_puthex(peer_var[CP_INITKEY].text,
- peer->recauto.key);
+ ctl_putint(peer_var[CP_INITSEQ].text, ap->seq);
+ ctl_puthex(peer_var[CP_INITKEY].text, ap->key);
ctl_putuint(peer_var[CP_INITTSP].text,
- peer->recauto.tstamp);
+ ntohl(peer->recval.tstamp));
break;
-#endif /* PUBKEY */
+#endif /* OPENSSL */
}
}
@@ -1717,7 +1736,7 @@ ctl_putclock(
if (mustput || (clock_stat->haveflags & CLK_HAVEVAL2)) {
if (clock_stat->fudgeval1 > 1)
ctl_putadr(clock_var[CC_FUDGEVAL2].text,
- (u_int32)clock_stat->fudgeval2);
+ (u_int32)clock_stat->fudgeval2, NULL);
else
ctl_putid(clock_var[CC_FUDGEVAL2].text,
(char *)&clock_stat->fudgeval2);
@@ -1876,7 +1895,7 @@ ctl_getitem(
numctlbadpkts++;
msyslog(LOG_WARNING,
"Possible 'ntpdx' exploit from %s:%d (possibly spoofed)\n",
- inet_ntoa(rmt_addr->sin_addr), ntohs(rmt_addr->sin_port)
+ stoa(rmt_addr), SRCPORT(rmt_addr)
);
return (0);
}
@@ -1884,10 +1903,10 @@ ctl_getitem(
if (cp < reqend)
cp++;
*tp-- = '\0';
- while (tp > buf) {
- *tp-- = '\0';
+ while (tp >= buf) {
if (!isspace((int)(*tp)))
break;
+ *tp-- = '\0';
}
reqpt = cp;
*data = buf;
@@ -2130,7 +2149,7 @@ write_variables(
register struct ctl_var *v;
register int ext_var;
char *valuep;
- long val;
+ long val = 0;
/*
* If he's trying to write into a peer tell him no way
@@ -2434,7 +2453,7 @@ unset_trap(
*/
int
ctlsettrap(
- struct sockaddr_in *raddr,
+ struct sockaddr_storage *raddr,
struct interface *linter,
int traptype,
int version
@@ -2539,7 +2558,7 @@ ctlsettrap(
tptouse->tr_sequence = 1;
tptouse->tr_addr = *raddr;
tptouse->tr_localaddr = linter;
- tptouse->tr_version = version;
+ tptouse->tr_version = (u_char) version;
tptouse->tr_flags = TRAP_INUSE;
if (traptype == TRAP_TYPE_CONFIG)
tptouse->tr_flags |= TRAP_CONFIGURED;
@@ -2555,7 +2574,7 @@ ctlsettrap(
*/
int
ctlclrtrap(
- struct sockaddr_in *raddr,
+ struct sockaddr_storage *raddr,
struct interface *linter,
int traptype
)
@@ -2580,18 +2599,18 @@ ctlclrtrap(
*/
static struct ctl_trap *
ctlfindtrap(
- struct sockaddr_in *raddr,
+ struct sockaddr_storage *raddr,
struct interface *linter
)
{
register struct ctl_trap *tp;
for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
- if (tp->tr_flags & TRAP_INUSE && NSRCADR(raddr) ==
- NSRCADR(&tp->tr_addr) && NSRCPORT(raddr) ==
- NSRCPORT(&tp->tr_addr) && linter ==
- tp->tr_localaddr)
- return (tp);
+ if ((tp->tr_flags & TRAP_INUSE)
+ && (NSRCPORT(raddr) == NSRCPORT(&tp->tr_addr))
+ && SOCKCMP(raddr, &tp->tr_addr)
+ && (linter == tp->tr_localaddr) )
+ return (tp);
}
return (struct ctl_trap *)NULL;
}
@@ -2612,7 +2631,7 @@ report_event(
* Record error code in proper spots, but have mercy on the
* log file.
*/
- if (!(err & PEER_EVENT)) {
+ if (!(err & (PEER_EVENT | CRPT_EVENT))) {
if (ctl_sys_num_events < CTL_SYS_MAXEVENTS)
ctl_sys_num_events++;
if (ctl_sys_last_event != (u_char)err) {
@@ -2634,10 +2653,10 @@ report_event(
#ifdef REFCLOCK
if (ISREFCLOCKADR(&peer->srcadr))
- src = refnumtoa(peer->srcadr.sin_addr.s_addr);
+ src = refnumtoa(&peer->srcadr);
else
#endif
- src = ntoa(&peer->srcadr);
+ src = stoa(&peer->srcadr);
peer->last_event = (u_char)(err & ~PEER_EVENT);
if (peer->num_events < CTL_PEER_MAXEVENTS)
@@ -2690,10 +2709,10 @@ report_event(
* variables. Don't send crypto strings.
*/
for (i = 1; i <= CS_MAXCODE; i++) {
-#ifdef PUBKEY
+#ifdef OPENSSL
if (i > CS_VARLIST)
continue;
-#endif /* PUBKEY */
+#endif /* OPENSSL */
ctl_putsys(i);
}
#ifdef REFCLOCK
@@ -2719,7 +2738,7 @@ report_event(
strlen(kv->text), 0);
free_varlist(clock_stat.kv_list);
}
-#endif /*REFCLOCK*/
+#endif /* REFCLOCK */
} else {
rpkt.associd = htons(peer->associd);
rpkt.status = htons(ctlpeerstatus(peer));
@@ -2727,12 +2746,13 @@ report_event(
/*
* Dump it all. Later, maybe less.
*/
- for (i = 1; i <= CP_MAXCODE; i++)
-#ifdef PUBKEY
+ for (i = 1; i <= CP_MAXCODE; i++) {
+#ifdef OPENSSL
if (i > CP_VARLIST)
continue;
-#endif /* PUBKEY */
+#endif /* OPENSSL */
ctl_putpeer(i, peer);
+ }
#ifdef REFCLOCK
/*
* for clock exception events: add clock variables to
@@ -2758,7 +2778,7 @@ report_event(
strlen(kv->text), 0);
free_varlist(clock_stat.kv_list);
}
-#endif /*REFCLOCK*/
+#endif /* REFCLOCK */
}
/*
@@ -2812,7 +2832,7 @@ char *
add_var(
struct ctl_var **kv,
u_long size,
- int def
+ u_short def
)
{
register u_long c;
@@ -2841,7 +2861,7 @@ set_var(
struct ctl_var **kv,
const char *data,
u_long size,
- int def
+ u_short def
)
{
register struct ctl_var *k;
@@ -2852,7 +2872,8 @@ set_var(
if (!data || !size)
return;
- if ((k = *kv)) {
+ k = *kv;
+ if (k != NULL) {
while (!(k->flags & EOV)) {
s = data;
t = k->text;
@@ -2887,7 +2908,7 @@ void
set_sys_var(
char *data,
u_long size,
- int def
+ u_short def
)
{
set_var(&ext_sys_var, data, size, def);
OpenPOWER on IntegriCloud