From 0d7f771778ef2a5065eb4c637854d9e4ef003e77 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Sat, 21 Feb 2009 21:49:19 +0000 Subject: [PATCH] Implement %h escape in sudoers include filenames. --- sudoers.pod | 9 ++++++++- toke.l | 31 ++++++++++++++++++++++++++----- visudo.c | 28 +++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/sudoers.pod b/sudoers.pod index 6767e7e1b..bca770f09 100644 --- a/sudoers.pod +++ b/sudoers.pod @@ -447,7 +447,7 @@ useful, for example, for keeping a site-wide I file in addition to a per-machine local one. For the sake of this example the site-wide I will be F and the per-machine one will be F. To include F -from F we would use the following line in F: +from within F we would use the following line in F: #include /etc/sudoers.local @@ -458,6 +458,13 @@ F will be processed. Files that are included may themselves include other files. A hard limit of 128 nested include files is enforced to prevent include file loops. +The filename may include the C<%h> escape, signifying the short form +of the hostname. I.e., if the machine's hostname is "xerxes", then + + #include /etc/sudoers.%h + +will cause B to include the file F. + =head2 Other special characters and reserved words The pound sign ('#') is used to indicate a comment (unless it is diff --git a/toke.l b/toke.l index 23be98c4b..90a5100b4 100644 --- a/toke.l +++ b/toke.l @@ -671,22 +671,43 @@ parse_include(base) char *base; { char *cp, *ep, *path; - int len; + int len = 0, subst = 0; + size_t shost_len = 0; /* Pull out path from #include line. */ cp = base + sizeof("#include"); while (isblank((unsigned char) *cp)) cp++; ep = cp; - while (*ep != '\0' && !isspace((unsigned char) *ep)) + while (*ep != '\0' && !isspace((unsigned char) *ep)) { + if (ep[0] == '%' && ep[1] == 'h') { + shost_len = strlen(user_shost); + len += shost_len - 2; + subst = 1; + } ep++; + } /* Make a copy of path and return it. */ - len = (int)(ep - cp); + len += (int)(ep - cp); if ((path = malloc(len + 1)) == NULL) yyerror("unable to allocate memory"); - memcpy(path, cp, len); - path[len] = '\0'; + if (subst) { + /* substitute for %h */ + char *pp = path; + while (cp < ep) { + if (cp[0] == '%' && cp[1] == 'h') { + memcpy(pp, user_shost, shost_len); + pp += shost_len; + cp += 2; + } + *pp++ = *cp++; + } + *pp = '\0'; + } else { + memcpy(path, cp, len); + path[len] = '\0'; + } /* Push any excess characters (e.g. comment, newline) back to the lexer */ if (*ep != '\0') diff --git a/visudo.c b/visudo.c index 235179f16..bbb97a827 100644 --- a/visudo.c +++ b/visudo.c @@ -105,6 +105,7 @@ struct sudoersfile { static RETSIGTYPE quit __P((int)); static char *get_args __P((char *)); static char *get_editor __P((char **)); +static void get_hostname __P((void)); static char whatnow __P((void)); static int check_aliases __P((int)); static int check_syntax __P((char *, int, int)); @@ -195,9 +196,10 @@ main(argc, argv) sudo_setgrent(); /* Mock up a fake sudo_user struct. */ - user_host = user_shost = user_cmnd = ""; + user_cmnd = ""; if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL) errorx(1, "you don't exist in the passwd database"); + get_hostname(); /* Setup defaults data structures. */ init_defaults(); @@ -908,6 +910,30 @@ get_args(cmnd) return(*args ? args : NULL); } +/* + * Look up the hostname and set user_host and user_shost. + */ +static void +get_hostname() +{ + char *p, thost[MAXHOSTNAMELEN + 1]; + + if (gethostname(thost, sizeof(thost)) != 0) { + user_host = user_shost = "localhost"; + return; + } + thost[sizeof(thost) - 1] = '\0'; + user_host = estrdup(thost); + + if ((p = strchr(user_host, '.'))) { + *p = '\0'; + user_shost = estrdup(user_host); + *p = '.'; + } else { + user_shost = user_host; + } +} + /* * Iterate through the sudoers datastructures looking for undefined * aliases or unused aliases. -- 2.40.0