1/* 2 * Copyright (c) 2007-2011 Todd C. Miller <Todd.Miller@courtesan.com> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17#include <config.h> 18 19#include <sys/types.h> 20#include <sys/param.h> 21#include <stdio.h> 22#ifdef STDC_HEADERS 23# include <stdlib.h> 24# include <stddef.h> 25#else 26# ifdef HAVE_STDLIB_H 27# include <stdlib.h> 28# endif 29#endif /* STDC_HEADERS */ 30#ifdef HAVE_STRING_H 31# include <string.h> 32#endif /* HAVE_STRING_H */ 33#ifdef HAVE_STRINGS_H 34# include <strings.h> 35#endif /* HAVE_STRINGS_H */ 36#ifdef HAVE_UNISTD_H 37# include <unistd.h> 38#endif /* HAVE_UNISTD_H */ 39#include <pwd.h> 40#include <grp.h> 41#include <ctype.h> 42 43#include "sudo.h" 44#include "lbuf.h" 45 46extern struct sudo_nss sudo_nss_file; 47#ifdef HAVE_LDAP 48extern struct sudo_nss sudo_nss_ldap; 49#endif 50 51#if defined(HAVE_LDAP) && defined(_PATH_NSSWITCH_CONF) 52/* 53 * Read in /etc/nsswitch.conf 54 * Returns a tail queue of matches. 55 */ 56struct sudo_nss_list * 57sudo_read_nss() 58{ 59 FILE *fp; 60 char *cp; 61 int saw_files = FALSE; 62 int saw_ldap = FALSE; 63 int got_match = FALSE; 64 static struct sudo_nss_list snl; 65 66 if ((fp = fopen(_PATH_NSSWITCH_CONF, "r")) == NULL) 67 goto nomatch; 68 69 while ((cp = sudo_parseln(fp)) != NULL) { 70 /* Skip blank or comment lines */ 71 if (*cp == '\0') 72 continue; 73 74 /* Look for a line starting with "sudoers:" */ 75 if (strncasecmp(cp, "sudoers:", 8) != 0) 76 continue; 77 78 /* Parse line */ 79 for ((cp = strtok(cp + 8, " \t")); cp != NULL; (cp = strtok(NULL, " \t"))) { 80 if (strcasecmp(cp, "files") == 0 && !saw_files) { 81 tq_append(&snl, &sudo_nss_file); 82 got_match = TRUE; 83 } else if (strcasecmp(cp, "ldap") == 0 && !saw_ldap) { 84 tq_append(&snl, &sudo_nss_ldap); 85 got_match = TRUE; 86 } else if (strcasecmp(cp, "[NOTFOUND=return]") == 0 && got_match) { 87 /* NOTFOUND affects the most recent entry */ 88 tq_last(&snl)->ret_if_notfound = TRUE; 89 got_match = FALSE; 90 } else if (strcasecmp(cp, "[SUCCESS=return]") == 0 && got_match) { 91 /* SUCCESS affects the most recent entry */ 92 tq_last(&snl)->ret_if_found = TRUE; 93 got_match = FALSE; 94 } else 95 got_match = FALSE; 96 } 97 /* Only parse the first "sudoers:" line */ 98 break; 99 } 100 fclose(fp); 101 102nomatch: 103 /* Default to files only if no matches */ 104 if (tq_empty(&snl)) 105 tq_append(&snl, &sudo_nss_file); 106 107 return &snl; 108} 109 110#else /* HAVE_LDAP && _PATH_NSSWITCH_CONF */ 111 112# if defined(HAVE_LDAP) && defined(_PATH_NETSVC_CONF) 113 114/* 115 * Read in /etc/netsvc.conf (like nsswitch.conf on AIX) 116 * Returns a tail queue of matches. 117 */ 118struct sudo_nss_list * 119sudo_read_nss() 120{ 121 FILE *fp; 122 char *cp, *ep; 123 int saw_files = FALSE; 124 int saw_ldap = FALSE; 125 int got_match = FALSE; 126 static struct sudo_nss_list snl; 127 128 if ((fp = fopen(_PATH_NETSVC_CONF, "r")) == NULL) 129 goto nomatch; 130 131 while ((cp = sudo_parseln(fp)) != NULL) { 132 /* Skip blank or comment lines */ 133 if (*cp == '\0') 134 continue; 135 136 /* Look for a line starting with "sudoers = " */ 137 if (strncasecmp(cp, "sudoers", 7) != 0) 138 continue; 139 cp += 7; 140 while (isspace((unsigned char)*cp)) 141 cp++; 142 if (*cp++ != '=') 143 continue; 144 145 /* Parse line */ 146 for ((cp = strtok(cp, ",")); cp != NULL; (cp = strtok(NULL, ","))) { 147 /* Trim leading whitespace. */ 148 while (isspace((unsigned char)*cp)) 149 cp++; 150 151 if (!saw_files && strncasecmp(cp, "files", 5) == 0 && 152 (isspace((unsigned char)cp[5]) || cp[5] == '\0')) { 153 tq_append(&snl, &sudo_nss_file); 154 got_match = TRUE; 155 ep = &cp[5]; 156 } else if (!saw_ldap && strncasecmp(cp, "ldap", 4) == 0 && 157 (isspace((unsigned char)cp[4]) || cp[4] == '\0')) { 158 tq_append(&snl, &sudo_nss_ldap); 159 got_match = TRUE; 160 ep = &cp[4]; 161 } else { 162 got_match = FALSE; 163 } 164 165 /* check for = auth qualifier */ 166 if (got_match && *ep) { 167 cp = ep; 168 while (isspace((unsigned char)*cp) || *cp == '=') 169 cp++; 170 if (strncasecmp(cp, "auth", 4) == 0 && 171 (isspace((unsigned char)cp[4]) || cp[4] == '\0')) { 172 tq_last(&snl)->ret_if_found = TRUE; 173 } 174 } 175 } 176 /* Only parse the first "sudoers" line */ 177 break; 178 } 179 fclose(fp); 180 181nomatch: 182 /* Default to files only if no matches */ 183 if (tq_empty(&snl)) 184 tq_append(&snl, &sudo_nss_file); 185 186 return &snl; 187} 188 189# else /* !_PATH_NETSVC_CONF && !_PATH_NSSWITCH_CONF */ 190 191/* 192 * Non-nsswitch.conf version with hard-coded order. 193 */ 194struct sudo_nss_list * 195sudo_read_nss() 196{ 197 static struct sudo_nss_list snl; 198 199# ifdef HAVE_LDAP 200 tq_append(&snl, &sudo_nss_ldap); 201# endif 202 tq_append(&snl, &sudo_nss_file); 203 204 return &snl; 205} 206 207# endif /* !HAVE_LDAP || !_PATH_NETSVC_CONF */ 208 209#endif /* HAVE_LDAP && _PATH_NSSWITCH_CONF */ 210 211/* Reset user_groups based on passwd entry. */ 212static void 213reset_groups(pw) 214 struct passwd *pw; 215{ 216#if defined(HAVE_INITGROUPS) && defined(HAVE_GETGROUPS) 217 if (pw != sudo_user.pw) { 218# ifdef HAVE_SETAUTHDB 219 aix_setauthdb(pw->pw_name); 220# endif 221 if (initgroups(pw->pw_name, pw->pw_gid) == -1) 222 log_fatal(USE_ERRNO|MSG_ONLY, "can't reset group vector"); 223 efree(user_groups); 224 user_groups = NULL; 225 if ((user_ngroups = getgroups(0, NULL)) > 0) { 226 user_groups = emalloc2(user_ngroups, sizeof(GETGROUPS_T)); 227 if (getgroups(user_ngroups, user_groups) < 0) 228 log_fatal(USE_ERRNO|MSG_ONLY, "can't get group vector"); 229 } 230# ifdef HAVE_SETAUTHDB 231 aix_restoreauthdb(); 232# endif 233 } 234#endif /* HAVE_INITGROUPS && HAVE_GETGROUPS */ 235} 236 237static int 238output(buf) 239 const char *buf; 240{ 241 return fputs(buf, stdout); 242} 243 244/* 245 * Print out privileges for the specified user. 246 * We only get here if the user is allowed to run something on this host. 247 */ 248void 249display_privs(snl, pw) 250 struct sudo_nss_list *snl; 251 struct passwd *pw; 252{ 253 struct sudo_nss *nss; 254 struct lbuf defs, privs; 255 int count, olen; 256 257 /* Reset group vector so group matching works correctly. */ 258 reset_groups(pw); 259 260 lbuf_init(&defs, output, 4, NULL); 261 lbuf_init(&privs, output, 4, NULL); 262 263 /* Display defaults from all sources. */ 264 lbuf_append(&defs, "Matching Defaults entries for %s on this host:\n", 265 pw->pw_name); 266 count = 0; 267 tq_foreach_fwd(snl, nss) { 268 count += nss->display_defaults(nss, pw, &defs); 269 } 270 if (count) 271 lbuf_append(&defs, "\n\n"); 272 else 273 defs.len = 0; 274 275 /* Display Runas and Cmnd-specific defaults from all sources. */ 276 olen = defs.len; 277 lbuf_append(&defs, "Runas and Command-specific defaults for %s:\n", 278 pw->pw_name); 279 count = 0; 280 tq_foreach_fwd(snl, nss) { 281 count += nss->display_bound_defaults(nss, pw, &defs); 282 } 283 if (count) 284 lbuf_append(&defs, "\n\n"); 285 else 286 defs.len = olen; 287 288 /* Display privileges from all sources. */ 289 lbuf_append(&privs, 290 "User %s may run the following commands on this host:\n", pw->pw_name); 291 count = 0; 292 tq_foreach_fwd(snl, nss) { 293 count += nss->display_privs(nss, pw, &privs); 294 } 295 if (count == 0) { 296 defs.len = 0; 297 privs.len = 0; 298 lbuf_append(&privs, "User %s is not allowed to run sudo on %s.\n", 299 pw->pw_name, user_shost); 300 } 301 lbuf_print(&defs); 302 lbuf_print(&privs); 303 304 lbuf_destroy(&defs); 305 lbuf_destroy(&privs); 306} 307 308/* 309 * Check user_cmnd against sudoers and print the matching entry if the 310 * command is allowed. 311 */ 312int 313display_cmnd(snl, pw) 314 struct sudo_nss_list *snl; 315 struct passwd *pw; 316{ 317 struct sudo_nss *nss; 318 319 /* Reset group vector so group matching works correctly. */ 320 reset_groups(pw); 321 322 tq_foreach_fwd(snl, nss) { 323 if (nss->display_cmnd(nss, pw) == 0) 324 return TRUE; 325 } 326 return FALSE; 327} 328