# $Id: stampproto.m,v 1.9 2012/11/02 21:56:36 ksb Exp $ %i static const char acProtoVer[] = "1\n"; /* Default timestamp top-level (no run-time override). */ #if !defined(STAMP_PATHFMT) #define STAMP_PATHFMT "/var/op/%s" #endif /* Params to credential a given escalation are encode in this variable. * For example under op we might ask for a wheel stamp for the real * login name with the same tty as we have now: * $STAMP_SPEC=wheel/$l:TTY=$y */ #if !defined(STAMP_SPEC) #define STAMP_SPEC "STAMP_SPEC" #endif /* Copy tableau values to the environment of the escalated process: * Note that this only works with op versions > 2.144. * $STAMP_SET=TERM:TERMCAP:EDITOR * No default for this. */ #if !defined(STAMP_SET) #define STAMP_SET "STAMP_SET" #endif /* Default credential search is to look for STAMP_PATHFMT applied * to our real uid as a decimal number (/var/op/7890) and look for * no special name-value-pair. */ #if !defined(STAMP_DEFREQ) #define STAMP_DEFREQ ":" #endif /* We build a socket, so we need a (small) listen queue length */ #if !defined(STAMP_LISTENQ) #define STAMP_LISTENQ 13 #endif /* Add these variable when set in the environment, for example to force * the name of the login that authenticated the session: * $AUTH_LOGIN=$l */ #if !defined(STAMP_REVEAL) #define STAMP_REVEAL "STAMP_REVEAL" #endif /* We can separate authentications into subdirs to allow more than * one policy for each login. Add a directory to the path with * this specification. The default is none. For example "blotto" * makes the path me /var/op/stamp/blotto/200 for an owner with * uid 200. */ #if !defined(STAMP_FACILITY) #define STAMP_FACILITY "STAMP_FACILITY" #endif #if !defined(STAMP_DEFAUTH_FACILITY) #define STAMP_DEFAUTH_FACILITY "stamp" #endif /* When a remote stamp wants to pass params to the roap service they * are sent via this environment variable. */ #if !defined(STAMP_QUERY) #define STAMP_QUERY "STAMP_QUERY" #endif /* What to tell the customer that failed to credential, if not the * default "authentication failed". (I like "Sorry.") */ #if !defined(STAMP_WARN) #define STAMP_WARN "STAMP_WARN" #endif /* Stamp status options TBS is "Is is ok To Build Stamp?" */ #define TBS_NOSYS 'S' /* no system support installed */ #define TBS_AVAIL 'A' /* no socket for login/uid/gid */ #define TBS_SEEN 'H' /* socket exists */ #define TBS_OK 'O' /* socket is up and authentic */ #define TBS_PENALTY 'P' /* socket denys access, penalty */ #define TBS_MESSAGE 'M' /* system message denys access */ #define TBS_ERROR 'E' /* misconfigured directory */ #define TBS_DIR 'D' /* is a directory, not a socket */ /* Some older hosts do not have PF_LOCAL or AF_LOCAL, use UNIX */ #if !defined(PF_LOCAL) #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #define PF_LOCAL AF_LOCAL #endif #if !defined(HAVE_SETPROCTITLE) #define HAVE_SETPROCTITLE (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__) #endif %% %i static const char rcsAPI[] = "$Id: stampproto.m,v 1.9 2012/11/02 21:56:36 ksb Exp $"; typedef struct SInode { uid_t uOwner; gid_t gOwner; mode_t mOwner; } StampInfo; static int Climbing(register char *); static const char *SetCache(); static int SysStatus(char *, char **, char **, StampInfo *); static int Client(char *pcEndPt); static char *SockSpec(char *, uid_t); static char *StampPath(const char *, char *); static const char acStampFmt[] = STAMP_PATHFMT, *pcStampFacility = STAMP_FACILITY; %% %c /* locate the cache directory from the environment (ksb) * pcStampFacility ($STAMP_FACILITY) is set to an absolute path we use it * as the prefix, else we use it as a faclility. * When not set we use a default faclility ("stamp") which could also be * an absolute path (like /var/non-existing-thing). */ static const char * SetCache() { register const char *pcFacility; auto char acBuild[MAXPATHLEN+4]; if ((char *)0 == (pcFacility = getenv(pcStampFacility))) { pcFacility = acDefAuthFacility; } if ('/' == pcFacility[0]) { return pcFacility; } snprintf(acBuild, sizeof(acBuild), acStampFmt, pcFacility); return strdup(acBuild); } /* Return the status of the timestamp structure (ksb) * We write on pcTop to process the path, but we put it back. */ static int SysStatus(char *pcTop, char **ppcText, char **ppcMessage, StampInfo *pSI) { auto struct stat stTop; auto char *pc_1, *pc_2; /* avoid bad params by replacing NULLs with diversions to noplace */ if ((char **)0 == ppcText) ppcText = &pc_1; if ((char **)0 == ppcMessage) ppcMessage = &pc_2; *ppcMessage = *ppcText = (char *)0; /* Set default perms */ pSI->uOwner = -1; pSI->gOwner = -1; pSI->mOwner = 0666; if ((const char *)0 == pcTop) { *ppcText = "no stamp name"; return TBS_NOSYS; } /* If the tail doesn't exist see if the parent does */ if (-1 == stat(pcTop, &stTop)) { register char *pcSlash; register int iCall; if (errno != ENOENT) { *ppcText = strerror(errno); return TBS_NOSYS; } if ((char *)0 != (pcSlash = strrchr(pcTop, '/'))) { while (pcSlash > pcTop && '/' == pcSlash[-1]) --pcSlash; } if ((char *)0 == pcSlash || pcSlash == pcTop) { *ppcText = "no such directory"; return TBS_NOSYS; } *pcSlash = '\000'; iCall = SysStatus(pcTop, ppcText, ppcMessage, pSI); *pcSlash = '/'; if (TBS_DIR == iCall) return TBS_AVAIL; return iCall; } pSI->uOwner = stTop.st_uid; pSI->gOwner = stTop.st_gid ; pSI->mOwner = stTop.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if (S_ISSOCK(stTop.st_mode)) { return TBS_SEEN; } if (S_ISDIR(stTop.st_mode)) { return TBS_DIR; } if (S_ISREG(stTop.st_mode)) { *ppcText = "timestamp support disabled by message file"; *ppcMessage = pcTop; return TBS_MESSAGE; } if (0 == (stTop.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH))) { *ppcText = "timestamp support disabled by permissions"; /* no message, sadly */ return TBS_MESSAGE; } *ppcText = "timestamp support disabled non-directory"; return TBS_ERROR; } /* Open a connection to the named domain socket (ksb) */ static int Client(char *pcEndPt) { auto struct sockaddr_un run; register int sRet, e; memset((void *)& run, '\000', sizeof(run)); run.sun_family = AF_UNIX; if (strlen(pcEndPt) > sizeof(run.sun_path)-1) { errno = ENAMETOOLONG; return -1; } (void)strcpy(run.sun_path, pcEndPt); if (-1 == (sRet = socket(AF_UNIX, SOCK_STREAM, 0))) { return -1; } if (-1 != connect(sRet, (struct sockaddr *)&run, strlen(pcEndPt)+2)) { (void)fcntl(sRet, F_SETFD, 1); return sRet; } e = errno; close(sRet); errno = e; return -1; } /* Return 1 when the path tries to climb ".." (ksb) */ static int Climbing(register char *pcArg) { register size_t w = strlen(pcArg); return 0 == strcmp("..", pcArg) || 0 == strncmp(pcArg, "../", 3) || (char *)0 != strstr(pcArg, "/../") || (w >= 3 && 0 == strcmp(pcArg+w-3, "/..")); } /* Take a name for the socket in the facility directory, or make one (ksb) * We won't accept ../foo, change the facility to an absolute path. */ static char * SockSpec(char *pc, uid_t wDefOwner) { auto char acCvt[64]; while ((char *)0 != pc && '.' == pc[0] && '/' == pc[1]) { while ('/' == *++pc) /* skip */; } if ((char *)0 == pc || '\000' == *pc) { snprintf(acCvt, sizeof(acCvt), "%ld", (long)wDefOwner); return strdup(acCvt); } if (Climbing(pc)) { fprintf(stderr, "%s: %s: climbing not allowed\n", progname, pc); printf("%d\n", EX_CONFIG); exit(EX_CONFIG); } return pc; } /* Given a socket specification find the stamp for it (ksb) * If the specificaion it the empty string, call SockSpec to get the * default before you call this function. */ static char * StampPath(const char *pcSpec, char *pcName) { auto char acPath[MAXPATHLEN+4]; if ('/' == pcName[0]) return pcName; if ((char *)0 == pcSpec && (char *)0 == (pcSpec = SetCache())) return (char *)0; snprintf(acPath, sizeof(acPath), "%s/%s", pcSpec, pcName); return strdup(acPath); } %%