void die_mail_pame(cl_t * cl, int pamerrno, struct passwd *pas, char *str, env_list_t * env) /* log an error in syslog, mail user if necessary, and die */ { char buf[MAX_MSG]; snprintf(buf, sizeof(buf), "%s for user '%s'", str, pas->pw_name); if (is_mail(cl->cl_option)) { char **envp = env_list_export_envp(env); FILE *mailf = create_mail(cl, "Could not run fcron job", NULL, NULL, envp); /* print the error in both syslog and a file, in order to mail it to user */ if (dup2(fileno(mailf), 1) != 1 || dup2(1, 2) != 2) die_e("dup2() error"); /* dup2 also clears close-on-exec flag */ foreground = 1; error_pame(pamh, pamerrno, buf, cl->cl_shell); error("Job '%s' has *not* run.", cl->cl_shell); foreground = 0; pam_end(pamh, pamerrno); become_user(cl, pas, "/"); launch_mailer(cl, mailf, envp); /* launch_mailer() does not return : we never get here */ } else die_pame(pamh, pamerrno, buf, cl->cl_shell); }
/* BTW, in case you're wondering, this returns void* so that * it can be used as a pthread... */ static void *default_start_server(void *arg) { server_t self = arg; connection_t con; int i; if (!self) return NULL; /* * TODO. * This shouldn't be done here. It doesn't really matter * because it is basically a no-op right now anyway. * A single process can have several servers running * in it, conceivably. * * Maybe it should be moved to ag.c and left up to main. */ become_daemon(self); /* configure the wait_queue's max size */ self->wq->max = self->maxcon; /* this is the connection handler: */ self->handlers = ag_malloc(sizeof(handler_rec) * self->numthreads); for (i = 0; i < self->numthreads; i++) { handler_t hd; hd = self->newhandler(); hd->svr = self; self->handlers[i].hd = hd; (void) pthread_create(&self->handlers[i].tid, /* thread id */ NULL, /* attributes */ hd->start, /* function to run */ (void *)hd /* arg to pass to function */ ); } /* * must do this before changing uids, because we may want to bind * to a port < 1024 and then become a non-root user. * * TODO * At this point we should also open up the logfile. */ self->listen_sk = setup_connection(self); /* only change users if self->uid is non-zero (non-root) */ if (self->uid) become_user(self); for (;;) { con = accept_connection(self->listen_sk); if (!self->wq->add_con(self->wq, con)) con->destroy(con); } }
void test_become_user(void **state) { struct passwd *sssd; errno_t ret; pid_t pid, wpid; int status; /* Must root as root, real or fake */ assert_int_equal(geteuid(), 0); sssd = getpwnam("sssd"); assert_non_null(sssd); pid = fork(); if (pid == 0) { /* Change the UID in a child */ ret = become_user(sssd->pw_uid, sssd->pw_gid); assert_int_equal(ret, EOK); /* Make sure we have the requested UID and GID now and there * are no supplementary groups */ assert_int_equal(geteuid(), sssd->pw_uid); assert_int_equal(getegid(), sssd->pw_gid); assert_int_equal(getuid(), sssd->pw_uid); assert_int_equal(getgid(), sssd->pw_gid); /* Another become_user is a no-op */ ret = become_user(sssd->pw_uid, sssd->pw_gid); assert_int_equal(ret, EOK); assert_int_equal(getgroups(0, NULL), 0); exit(0); } assert_int_not_equal(pid, -1); wpid = waitpid(pid, &status, 0); assert_int_equal(wpid, pid); assert_true(WIFEXITED(status)); assert_int_equal(WEXITSTATUS(status), 0); }
void change_user_setup_env(struct cl_t *cl, char ***sendmailenv, char ***jobenv, char **curshell, char **curhome, char **content_type, char **encoding) /* call setup_user_and_env() and become_user(). * As a result, *curshell and *curhome will be allocated and should thus be freed * if curshell and curhome are not NULL. */ { struct passwd *pas; errno = 0; pas = getpwnam(cl->cl_runas); if (pas == NULL) die_e("failed to get passwd fields for user \"%s\"", cl->cl_runas); setup_user_and_env(cl, pas, sendmailenv, jobenv, curshell, curhome, content_type, encoding); become_user(cl, pas, (curhome != NULL) ? *curhome : "/"); }
/**************************************************************************** close a cnum ****************************************************************************/ void close_cnum(connection_struct *conn, uint16 vuid) { DirCacheFlush(SNUM(conn)); unbecome_user(); DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n", remote_machine,conn->client_address, lp_servicename(SNUM(conn)))); yield_connection(conn, lp_servicename(SNUM(conn)), lp_max_connections(SNUM(conn))); if (lp_status(SNUM(conn))) yield_connection(conn,"STATUS.",MAXSTATUS); file_close_conn(conn); dptr_closecnum(conn); /* execute any "postexec = " line */ if (*lp_postexec(SNUM(conn)) && become_user(conn, vuid)) { pstring cmd; pstrcpy(cmd,lp_postexec(SNUM(conn))); standard_sub(conn,cmd); smbrun(cmd,NULL,False); unbecome_user(); } unbecome_user(); /* execute any "root postexec = " line */ if (*lp_rootpostexec(SNUM(conn))) { pstring cmd; pstrcpy(cmd,lp_rootpostexec(SNUM(conn))); standard_sub(conn,cmd); smbrun(cmd,NULL,False); } conn_free(conn); }
static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_type) { struct share_mode_lock *lck = 0; BOOL delete_dir = False; NTSTATUS status = NT_STATUS_OK; /* * NT can set delete_on_close of the last open * reference to a directory also. */ lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_directory: Could not get share mode lock for %s\n", fsp->fsp_name)); return NT_STATUS_INVALID_PARAMETER; } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_directory: Could not delete share entry for %s\n", fsp->fsp_name)); } if (fsp->initial_delete_on_close) { BOOL became_user = False; /* Initial delete on close was set - for * directories we don't care if anyone else * wrote a real delete on close. */ if (current_user.vuid != fsp->vuid) { become_user(fsp->conn, fsp->vuid); became_user = True; } send_stat_cache_delete_message(fsp->fsp_name); set_delete_on_close_lck(lck, True, ¤t_user.ut); if (became_user) { unbecome_user(); } } delete_dir = lck->delete_on_close; if (delete_dir) { int i; /* See if others still have the dir open. If this is the * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->num_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; if (is_valid_share_mode_entry(e)) { if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } delete_dir = False; break; } } } if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) && delete_dir && lck->delete_token) { /* Become the user who requested the delete. */ if (!push_sec_ctx()) { smb_panic("close_directory: failed to push sec_ctx.\n"); } set_sec_ctx(lck->delete_token->uid, lck->delete_token->gid, lck->delete_token->ngroups, lck->delete_token->groups, NULL); TALLOC_FREE(lck); status = rmdir_internals(fsp->conn, fsp->fsp_name); DEBUG(5,("close_directory: %s. Delete on close was set - " "deleting directory returned %s.\n", fsp->fsp_name, nt_errstr(status))); /* unbecome user. */ pop_sec_ctx(); /* * Ensure we remove any change notify requests that would * now fail as the directory has been deleted. */ if(NT_STATUS_IS_OK(status)) { remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING); } } else { TALLOC_FREE(lck); remove_pending_change_notify_requests_by_fid( fsp, NT_STATUS_OK); } /* * Do the code common to files and directories. */ close_filestruct(fsp); file_free(fsp); return status; }
static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; BOOL delete_file = False; struct share_mode_lock *lck; SMB_STRUCT_STAT sbuf; NTSTATUS status = NT_STATUS_OK; int ret; /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " "lock for file %s\n", fsp->fsp_name)); return NT_STATUS_INVALID_PARAMETER; } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share " "entry for file %s\n", fsp->fsp_name)); } if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) { BOOL became_user = False; /* Initial delete on close was set and no one else * wrote a real delete on close. */ if (current_user.vuid != fsp->vuid) { become_user(conn, fsp->vuid); became_user = True; } set_delete_on_close_lck(lck, True, ¤t_user.ut); if (became_user) { unbecome_user(); } } delete_file = lck->delete_on_close; if (delete_file) { int i; /* See if others still have the file open. If this is the * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->num_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; if (is_valid_share_mode_entry(e)) { if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } delete_file = False; break; } } } /* Notify any deferred opens waiting on this close. */ notify_deferred_opens(lck); reply_to_oplock_break_requests(fsp); /* * NT can set delete_on_close of the last open * reference to a file. */ if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) || !delete_file || (lck->delete_token == NULL)) { TALLOC_FREE(lck); return NT_STATUS_OK; } /* * Ok, we have to delete the file */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp->fsp_name)); /* Become the user who requested the delete. */ if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } set_sec_ctx(lck->delete_token->uid, lck->delete_token->gid, lck->delete_token->ngroups, lck->delete_token->groups, NULL); /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ if (fsp->posix_open) { ret = SMB_VFS_LSTAT(conn,fsp->fsp_name,&sbuf); } else { ret = SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf); } if (ret != 0) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and stat failed with error %s\n", fsp->fsp_name, strerror(errno) )); /* * Don't save the errno here, we ignore this error */ goto done; } if(sbuf.st_dev != fsp->dev || sbuf.st_ino != fsp->inode) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and dev and/or inode does not match\n", fsp->fsp_name )); DEBUG(5,("close_remove_share_mode: file %s. stored dev = %x, " "inode = %.0f stat dev = %x, inode = %.0f\n", fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, (unsigned int)sbuf.st_dev, (double)sbuf.st_ino )); /* * Don't save the errno here, we ignore this error */ goto done; } if (SMB_VFS_UNLINK(conn,fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp->fsp_name, strerror(errno) )); status = map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, fsp->fsp_name); /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode * entry - ensure we only delete once by resetting * the delete on close flag. JRA. */ set_delete_on_close_lck(lck, False, NULL); done: /* unbecome user. */ pop_sec_ctx(); TALLOC_FREE(lck); return status; }
int main(int argc, char **argv) { int interval = 30, i; const char *prog = xbasename(argv[0]); time_t now, nextpoll = 0; const char *user = NULL; struct passwd *new_uid = NULL; const char *pidfilebase = prog; logformat = DEFAULT_LOGFORMAT; user = RUN_AS_USER; printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); while ((i = getopt(argc, argv, "+hs:l:i:f:u:Vp:")) != -1) { switch(i) { case 'h': help(prog); break; case 's': monhost = optarg; break; case 'l': logfn = optarg; break; case 'i': interval = atoi(optarg); break; case 'f': logformat = optarg; break; case 'u': user = optarg; break; case 'V': exit(EXIT_SUCCESS); case 'p': pidfilebase = optarg; break; } } argc -= optind; argv += optind; /* not enough args for the old way? */ if ((argc == 1) || (argc == 2)) help(prog); /* see if it's being called in the old style - 3 or 4 args */ /* <system> <logfn> <interval> [<format>] */ if (argc >= 3) { monhost = argv[0]; logfn = argv[1]; interval = atoi(argv[2]); } if (argc >= 4) { /* read out the remaining argv entries to the format string */ logformat = xmalloc(LARGEBUF); memset(logformat, '\0', LARGEBUF); for (i = 3; i < argc; i++) snprintfcat(logformat, LARGEBUF, "%s ", argv[i]); } if (!monhost) fatalx(EXIT_FAILURE, "No UPS defined for monitoring - use -s <system>"); if (!logfn) fatalx(EXIT_FAILURE, "No filename defined for logging - use -l <file>"); /* shouldn't happen */ if (!logformat) fatalx(EXIT_FAILURE, "No format defined - but this should be impossible"); printf("logging status of %s to %s (%is intervals)\n", monhost, logfn, interval); if (upscli_splitname(monhost, &upsname, &hostname, &port) != 0) { fatalx(EXIT_FAILURE, "Error: invalid UPS definition. Required format: upsname[@hostname[:port]]\n"); } if (upscli_connect(&ups, hostname, port, UPSCLI_CONN_TRYSSL) < 0) fprintf(stderr, "Warning: initial connect failed: %s\n", upscli_strerror(&ups)); if (strcmp(logfn, "-") == 0) logfile = stdout; else logfile = fopen(logfn, "a"); if (logfile == NULL) fatal_with_errno(EXIT_FAILURE, "could not open logfile %s", logfn); /* now drop root if we have it */ new_uid = get_user_pwent(user); open_syslog(prog); if (logfile != stdout) background(); setup_signals(); writepid(pidfilebase); become_user(new_uid); compile_format(); while (exit_flag == 0) { time(&now); if (nextpoll > now) { /* there is still time left, so sleep it off */ sleep(difftime(nextpoll, now)); nextpoll += interval; } else { /* we spent more time in polling than the interval allows */ nextpoll = now + interval; } if (reopen_flag) { upslogx(LOG_INFO, "Signal %d: reopening log file", reopen_flag); reopen_log(); reopen_flag = 0; } /* reconnect if necessary */ if (upscli_fd(&ups) < 0) { upscli_connect(&ups, hostname, port, 0); } run_flist(); /* don't keep connection open if we don't intend to use it shortly */ if (interval > 30) { upscli_disconnect(&ups); } } upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); if (logfile != stdout) fclose(logfile); upscli_disconnect(&ups); exit(EXIT_SUCCESS); }
static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; bool delete_file = false; bool changed_user = false; struct share_mode_lock *lck = NULL; NTSTATUS status = NT_STATUS_OK; NTSTATUS tmp_status; struct file_id id; /* Ensure any pending write time updates are done. */ if (fsp->update_write_time_event) { update_write_time_handler(smbd_event_context(), fsp->update_write_time_event, timeval_current(), (void *)fsp); } /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " "lock for file %s\n", fsp_str_dbg(fsp))); status = NT_STATUS_INVALID_PARAMETER; goto done; } if (fsp->write_time_forced) { DEBUG(10,("close_remove_share_mode: write time forced " "for file %s\n", fsp_str_dbg(fsp))); set_close_write_time(fsp, lck->changed_write_time); } else if (fsp->update_write_time_on_close) { /* Someone had a pending write. */ if (null_timespec(fsp->close_write_time)) { DEBUG(10,("close_remove_share_mode: update to current time " "for file %s\n", fsp_str_dbg(fsp))); /* Update to current time due to "normal" write. */ set_close_write_time(fsp, timespec_current()); } else { DEBUG(10,("close_remove_share_mode: write time pending " "for file %s\n", fsp_str_dbg(fsp))); /* Update to time set on close call. */ set_close_write_time(fsp, fsp->close_write_time); } } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share " "entry for file %s\n", fsp_str_dbg(fsp))); } if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) { bool became_user = False; /* Initial delete on close was set and no one else * wrote a real delete on close. */ if (current_user.vuid != fsp->vuid) { become_user(conn, fsp->vuid); became_user = True; } fsp->delete_on_close = true; set_delete_on_close_lck(lck, True, ¤t_user.ut); if (became_user) { unbecome_user(); } } delete_file = lck->delete_on_close; if (delete_file) { int i; /* See if others still have the file open. If this is the * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->num_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; if (is_valid_share_mode_entry(e)) { if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } delete_file = False; break; } } } /* Notify any deferred opens waiting on this close. */ notify_deferred_opens(lck); reply_to_oplock_break_requests(fsp); /* * NT can set delete_on_close of the last open * reference to a file. */ if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) || !delete_file || (lck->delete_token == NULL)) { TALLOC_FREE(lck); return NT_STATUS_OK; } /* * Ok, we have to delete the file */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp_str_dbg(fsp))); /* * Don't try to update the write time when we delete the file */ fsp->update_write_time_on_close = false; if (!unix_token_equal(lck->delete_token, ¤t_user.ut)) { /* Become the user who requested the delete. */ DEBUG(5,("close_remove_share_mode: file %s. " "Change user to uid %u\n", fsp_str_dbg(fsp), (unsigned int)lck->delete_token->uid)); if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } set_sec_ctx(lck->delete_token->uid, lck->delete_token->gid, lck->delete_token->ngroups, lck->delete_token->groups, NULL); changed_user = true; } /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ tmp_status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(tmp_status)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and stat failed with error %s\n", fsp_str_dbg(fsp), nt_errstr(tmp_status))); /* * Don't save the errno here, we ignore this error */ goto done; } id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); if (!file_id_equal(&fsp->file_id, &id)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and dev and/or inode does not match\n", fsp_str_dbg(fsp))); DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, " "stat file_id %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), file_id_string_tos(&id))); /* * Don't save the errno here, we ignore this error */ goto done; } if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); goto done; } } if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, fsp->fsp_name->base_name); /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode * entry - ensure we only delete once by resetting * the delete on close flag. JRA. */ fsp->delete_on_close = false; set_delete_on_close_lck(lck, False, NULL); done: if (changed_user) { /* unbecome user. */ pop_sec_ctx(); } TALLOC_FREE(lck); return status; }
/**************************************************************************** make a connection to a service ****************************************************************************/ connection_struct *make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid, int *ecode) { int snum; struct passwd *pass = NULL; BOOL guest = False; BOOL force = False; extern int Client; connection_struct *conn; int ret; strlower(service); snum = find_service(service); if (snum < 0) { extern int Client; if (strequal(service,"IPC$")) { DEBUG(3,("refusing IPC connection\n")); *ecode = ERRnoipc; return NULL; } DEBUG(0,("%s (%s) couldn't find service %s\n", remote_machine, client_addr(Client), service)); *ecode = ERRinvnetname; return NULL; } if (strequal(service,HOMES_NAME)) { if (*user && Get_Pwnam(user,True)) { fstring dos_username; fstrcpy(dos_username, user); unix_to_dos(dos_username, True); return(make_connection(dos_username,user,password, pwlen,dev,vuid,ecode)); } if(lp_security() != SEC_SHARE) { if (validated_username(vuid)) { fstring dos_username; fstrcpy(user,validated_username(vuid)); fstrcpy(dos_username, user); unix_to_dos(dos_username, True); return(make_connection(dos_username,user,password,pwlen,dev,vuid,ecode)); } } else { /* Security = share. Try with sesssetup_user * as the username. */ if(*sesssetup_user) { fstring dos_username; fstrcpy(user,sesssetup_user); fstrcpy(dos_username, user); unix_to_dos(dos_username, True); return(make_connection(dos_username,user,password,pwlen,dev,vuid,ecode)); } } } if (!lp_snum_ok(snum) || !check_access(Client, lp_hostsallow(snum), lp_hostsdeny(snum))) { *ecode = ERRaccess; return NULL; } /* you can only connect to the IPC$ service as an ipc device */ if (strequal(service,"IPC$")) pstrcpy(dev,"IPC"); if (*dev == '?' || !*dev) { if (lp_print_ok(snum)) { pstrcpy(dev,"LPT1:"); } else { pstrcpy(dev,"A:"); } } /* if the request is as a printer and you can't print then refuse */ strupper(dev); if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) { DEBUG(1,("Attempt to connect to non-printer as a printer\n")); *ecode = ERRinvdevice; return NULL; } /* lowercase the user name */ strlower(user); /* add it as a possible user name */ add_session_user(service); /* shall we let them in? */ if (!authorise_login(snum,user,password,pwlen,&guest,&force,vuid)) { DEBUG( 2, ( "Invalid username/password for %s\n", service ) ); *ecode = ERRbadpw; return NULL; } conn = conn_new(); if (!conn) { DEBUG(0,("Couldn't find free connection.\n")); *ecode = ERRnoresource; conn_free(conn); return NULL; } /* find out some info about the user */ pass = Get_Pwnam(user,True); if (pass == NULL) { DEBUG(0,( "Couldn't find account %s\n",user)); *ecode = ERRbaduid; conn_free(conn); return NULL; } conn->read_only = lp_readonly(snum); { pstring list; StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1); pstring_sub(list,"%S",service); if (user_in_list(user,list)) conn->read_only = True; StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1); pstring_sub(list,"%S",service); if (user_in_list(user,list)) conn->read_only = False; } /* admin user check */ /* JRA - original code denied admin user if the share was marked read_only. Changed as I don't think this is needed, but old code left in case there is a problem here. */ if (user_in_list(user,lp_admin_users(snum)) #if 0 && !conn->read_only #endif ) { conn->admin_user = True; DEBUG(0,("%s logged in as admin user (root privileges)\n",user)); } else { conn->admin_user = False; } conn->force_user = force; conn->vuid = vuid; conn->uid = pass->pw_uid; conn->gid = pass->pw_gid; safe_strcpy(conn->client_address, client_addr(Client), sizeof(conn->client_address)-1); conn->num_files_open = 0; conn->lastused = time(NULL); conn->service = snum; conn->used = True; conn->printer = (strncmp(dev,"LPT",3) == 0); conn->ipc = (strncmp(dev,"IPC",3) == 0); conn->dirptr = NULL; conn->veto_list = NULL; conn->hide_list = NULL; conn->veto_oplock_list = NULL; string_set(&conn->dirpath,""); string_set(&conn->user,user); /* * If force user is true, then store the * given userid and also the primary groupid * of the user we're forcing. */ if (*lp_force_user(snum)) { struct passwd *pass2; pstring fuser; pstrcpy(fuser,lp_force_user(snum)); /* Allow %S to be used by force user. */ pstring_sub(fuser,"%S",service); pass2 = (struct passwd *)Get_Pwnam(fuser,True); if (pass2) { conn->uid = pass2->pw_uid; conn->gid = pass2->pw_gid; string_set(&conn->user,fuser); fstrcpy(user,fuser); conn->force_user = True; DEBUG(3,("Forced user %s\n",fuser)); } else { DEBUG(1,("Couldn't find user %s\n",fuser)); } } #ifdef HAVE_GETGRNAM /* * If force group is true, then override * any groupid stored for the connecting user. */ if (*lp_force_group(snum)) { struct group *gptr; pstring gname; pstring tmp_gname; BOOL user_must_be_member = False; StrnCpy(tmp_gname,lp_force_group(snum),sizeof(pstring)-1); if (tmp_gname[0] == '+') { user_must_be_member = True; StrnCpy(gname,&tmp_gname[1],sizeof(pstring)-2); } else { StrnCpy(gname,tmp_gname,sizeof(pstring)-1); } /* default service may be a group name */ pstring_sub(gname,"%S",service); gptr = (struct group *)getgrnam(gname); if (gptr) { /* * If the user has been forced and the forced group starts * with a '+', then we only set the group to be the forced * group if the forced user is a member of that group. * Otherwise, the meaning of the '+' would be ignored. */ if (conn->force_user && user_must_be_member) { int i; for (i = 0; gptr->gr_mem[i] != NULL; i++) { if (strcmp(user,gptr->gr_mem[i]) == 0) { conn->gid = gptr->gr_gid; DEBUG(3,("Forced group %s for member %s\n",gname,user)); break; } } } else { conn->gid = gptr->gr_gid; DEBUG(3,("Forced group %s\n",gname)); } } else { DEBUG(1,("Couldn't find group %s\n",gname)); } } #endif /* HAVE_GETGRNAM */ { pstring s; pstrcpy(s,lp_pathname(snum)); standard_sub(conn,s); string_set(&conn->connectpath,s); DEBUG(3,("Connect path is %s\n",s)); } /* groups stuff added by ih */ conn->ngroups = 0; conn->groups = NULL; if (!IS_IPC(conn)) { /* Find all the groups this uid is in and store them. Used by become_user() */ setup_groups(conn->user,conn->uid,conn->gid, &conn->ngroups,&conn->groups); /* check number of connections */ if (!claim_connection(conn, lp_servicename(SNUM(conn)), lp_max_connections(SNUM(conn)), False)) { DEBUG(1,("too many connections - rejected\n")); *ecode = ERRnoresource; conn_free(conn); return NULL; } if (lp_status(SNUM(conn))) claim_connection(conn,"STATUS.", MAXSTATUS,False); } /* IS_IPC */ /* execute any "root preexec = " line */ if (*lp_rootpreexec(SNUM(conn))) { pstring cmd; pstrcpy(cmd,lp_rootpreexec(SNUM(conn))); standard_sub(conn,cmd); DEBUG(5,("cmd=%s\n",cmd)); ret = smbrun(cmd,NULL,False); if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) { DEBUG(1,("preexec gave %d - failing connection\n", ret)); conn_free(conn); *ecode = ERRsrverror; return NULL; } } if (!become_user(conn, conn->vuid)) { DEBUG(0,("Can't become connected user!\n")); if (!IS_IPC(conn)) { yield_connection(conn, lp_servicename(SNUM(conn)), lp_max_connections(SNUM(conn))); if (lp_status(SNUM(conn))) { yield_connection(conn,"STATUS.",MAXSTATUS); } } conn_free(conn); *ecode = ERRbadpw; return NULL; } if (dos_ChDir(conn->connectpath) != 0) { DEBUG(0,("Can't change directory to %s (%s)\n", conn->connectpath,strerror(errno))); unbecome_user(); if (!IS_IPC(conn)) { yield_connection(conn, lp_servicename(SNUM(conn)), lp_max_connections(SNUM(conn))); if (lp_status(SNUM(conn))) yield_connection(conn,"STATUS.",MAXSTATUS); } conn_free(conn); *ecode = ERRinvnetname; return NULL; } string_set(&conn->origpath,conn->connectpath); #if SOFTLINK_OPTIMISATION /* resolve any soft links early */ { pstring s; pstrcpy(s,conn->connectpath); dos_GetWd(s); string_set(&conn->connectpath,s); dos_ChDir(conn->connectpath); } #endif add_session_user(user); /* execute any "preexec = " line */ if (*lp_preexec(SNUM(conn))) { pstring cmd; pstrcpy(cmd,lp_preexec(SNUM(conn))); standard_sub(conn,cmd); ret = smbrun(cmd,NULL,False); if (ret != 0 && lp_preexec_close(SNUM(conn))) { DEBUG(1,("preexec gave %d - failing connection\n", ret)); conn_free(conn); *ecode = ERRsrverror; return NULL; } } /* * Print out the 'connected as' stuff here as we need * to know the effective uid and gid we will be using. */ if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { dbgtext( "%s (%s) ", remote_machine, conn->client_address ); dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) ); dbgtext( "as user %s ", user ); dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() ); dbgtext( "(pid %d)\n", (int)getpid() ); } /* we've finished with the sensitive stuff */ unbecome_user(); /* Add veto/hide lists */ if (!IS_IPC(conn) && !IS_PRINT(conn)) { set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn))); set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn))); set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn))); } return(conn); }
int run_job(struct exe_t *exeent) /* fork(), redirect outputs to a temp file, and execl() the task. * Return ERR if it could not fork() the first time, OK otherwise. */ { pid_t pid; cl_t *line = exeent->e_line; int pipe_pid_fd[2]; int ret = 0; /* prepare the job execution */ if (pipe(pipe_pid_fd) != 0) { error_e("pipe(pipe_pid_fd) : setting job_pid to -1"); exeent->e_job_pid = -1; pipe_pid_fd[0] = pipe_pid_fd[1] = -1; } #ifdef CHECKRUNJOB debug ("run_job(): first pipe created successfully : about to do first fork()"); #endif /* CHECKRUNJOB */ switch (pid = fork()) { case -1: error_e("Fork error : could not exec '%s'", line->cl_shell); return ERR; break; case 0: /* child */ { struct passwd *pas = NULL; char **jobenv = NULL; char **sendmailenv = NULL; char *curshell = NULL; char *curhome = NULL; char *content_type = NULL; char *encoding = NULL; FILE *mailf = NULL; int status = 0; int to_stdout = foreground && is_stdout(line->cl_option); int pipe_fd[2]; short int mailpos = 0; /* 'empty mail file' size */ #ifdef WITH_SELINUX int flask_enabled = is_selinux_enabled(); #endif /* // */ debug("run_job(): child: %s, output to %s, %s, %s\n", is_mail(line->cl_option) ? "mail" : "no mail", to_stdout ? "stdout" : "file", foreground ? "running in foreground" : "running in background", is_stdout(line->cl_option) ? "stdout" : "normal"); /* // */ errno = 0; pas = getpwnam(line->cl_runas); if (pas == NULL) die_e("failed to get passwd fields for user \"%s\"", line->cl_runas); setup_user_and_env(line, pas, &sendmailenv, &jobenv, &curshell, &curhome, &content_type, &encoding); /* close unneeded READ fd */ xclose_check(&(pipe_pid_fd[0]), "child's pipe_pid_fd[0]"); pipe_fd[0] = pipe_fd[1] = -1; if (!to_stdout && is_mail(line->cl_option)) { /* we create the temp file (if needed) before change_user(), * as temp_file() needs root privileges */ /* if we run in foreground, stdout and stderr point to the console. * Otherwise, stdout and stderr point to /dev/null . */ mailf = create_mail(line, NULL, content_type, encoding, jobenv); mailpos = ftell(mailf); if (pipe(pipe_fd) != 0) die_e("could not pipe() (job not executed)"); } become_user(line, pas, curhome); Free_safe(curhome); /* restore umask to default */ umask(saved_umask); sig_dfl(); #ifdef CHECKRUNJOB debug ("run_job(): child: change_user() done -- about to do 2nd fork()"); #endif /* CHECKRUNJOB */ /* now, run the job */ switch (pid = fork()) { case -1: error_e("Fork error : could not exec '%s'", line->cl_shell); if (write(pipe_pid_fd[1], &pid, sizeof(pid)) < 0) error_e("could not write child pid to pipe_pid_fd[1]"); xclose_check(&(pipe_fd[0]), "child's pipe_fd[0]"); xclose_check(&(pipe_fd[1]), "child's pipe_fd[1]"); xclose_check(&(pipe_pid_fd[1]), "child's pipe_pid_fd[1]"); exit(EXIT_ERR); break; case 0: /* grand child (child of the 2nd fork) */ /* the grand child does not use this pipe: close remaining fd */ xclose_check(&(pipe_pid_fd[1]), "grand child's pipe_pid_fd[1]"); if (!to_stdout) /* note : the following closes the pipe */ run_job_grand_child_setup_stderr_stdout(line, pipe_fd); foreground = 1; /* now, errors will be mailed to the user (or to /dev/null) */ run_job_grand_child_setup_nice(line); xcloselog(); #if defined(CHECKJOBS) || defined(CHECKRUNJOB) /* this will force to mail a message containing at least the exact * and complete command executed for each execution of all jobs */ debug("run_job(): grand-child: Executing \"%s -c %s\"", curshell, line->cl_shell); #endif /* CHECKJOBS OR CHECKRUNJOB */ #ifdef WITH_SELINUX if (flask_enabled && setexeccon(line->cl_file->cf_user_context) < 0) die_e("Can't set execute context '%s' for user '%s'.", line->cl_file->cf_user_context, line->cl_runas); #else if (setsid() == -1) { die_e("setsid(): errno %d", errno); } #endif execle(curshell, curshell, "-c", line->cl_shell, NULL, jobenv); /* execle returns only on error */ die_e("Couldn't exec shell '%s'", curshell); /* execution never gets here */ default: /* child (parent of the 2nd fork) */ /* close unneeded WRITE pipe and READ pipe */ xclose_check(&(pipe_fd[1]), "child's pipe_fd[1]"); #ifdef CHECKRUNJOB debug("run_job(): child: pipe_fd[1] and pipe_pid_fd[0] closed" " -- about to write grand-child pid to pipe"); #endif /* CHECKRUNJOB */ /* give the pid of the child to the parent (main) fcron process */ ret = write_pipe(pipe_pid_fd[1], &pid, sizeof(pid)); if (ret != OK) { if (ret == ERR) error ("run_job(): child: Could not write job pid to pipe"); else { errno = ret; error_e ("run_job(): child: Could not write job pid to pipe"); } } #ifdef CHECKRUNJOB debug("run_job(): child: grand-child pid written to pipe"); #endif /* CHECKRUNJOB */ if (!is_nolog(line->cl_option)) explain("Job '%s' started for user %s (pid %d)", line->cl_shell, line->cl_file->cf_user, pid); if (!to_stdout && is_mail(line->cl_option)) { /* user wants a mail : we use the pipe */ char mailbuf[TERM_LEN]; FILE *pipef = fdopen(pipe_fd[0], "r"); if (pipef == NULL) die_e("Could not fdopen() pipe_fd[0]"); mailbuf[sizeof(mailbuf) - 1] = '\0'; while (fgets(mailbuf, sizeof(mailbuf), pipef) != NULL) if (fputs(mailbuf, mailf) < 0) warn("fputs() failed to write to mail file for job '%s' (pid %d)", line->cl_shell, pid); /* (closes also pipe_fd[0]): */ xfclose_check(&pipef, "child's pipef"); } /* FIXME : FOLLOWING HACK USELESS ? */ /* FIXME : HACK * this is a try to fix the bug on sorcerer linux (no jobs * exectued at all, and * "Could not read job pid : setting it to -1: No child processes" * error messages) */ /* use a select() or similar to know when parent has read * the pid (with a timeout !) */ /* // */ sleep(2); /* // */ #ifdef CHECKRUNJOB debug("run_job(): child: closing pipe with parent"); #endif /* CHECKRUNJOB */ xclose_check(&(pipe_pid_fd[1]), "child's pipe_pid_fd[1]"); /* we use a while because of a possible interruption by a signal */ while ((pid = wait3(&status, 0, NULL)) > 0) { #ifdef CHECKRUNJOB debug("run_job(): child: ending job pid %d", pid); #endif /* CHECKRUNJOB */ end_job(line, status, mailf, mailpos, sendmailenv); } /* execution never gets here */ } /* execution should never gets here, but if it happened we exit with an error */ exit(EXIT_ERR); } default: /* parent */ /* close unneeded WRITE fd */ xclose_check(&(pipe_pid_fd[1]), "parent's pipe_pid_fd[1]"); exeent->e_ctrl_pid = pid; #ifdef CHECKRUNJOB debug("run_job(): about to read grand-child pid..."); #endif /* CHECKRUNJOB */ /* read the pid of the job */ ret = read_pipe(pipe_pid_fd[0], &(exeent->e_job_pid), sizeof(pid_t)); if (ret != OK) { if (ret == ERR) { error("Could not read job pid because of closed pipe:" " setting it to -1"); } else { errno = ret; error_e("Could not read job pid : setting it to -1"); } exeent->e_job_pid = -1; } xclose_check(&(pipe_pid_fd[0]), "parent's pipe_pid_fd[0]"); #ifdef CHECKRUNJOB debug ("run_job(): finished reading pid of the job -- end of run_job()."); #endif /* CHECKRUNJOB */ } return OK; }
/** * the main entrance of the colloctor process */ int main(int argc, char **argv) { struct sigaction act_full_A, act_full_B; /*change the user if we need*/ if(argc>=3&&0==strcmp(argv[1],"-u")) { become_user(argv[2]); } /*get the two buffers */ shmem_info_A = get_shmem_info(0); shmem_info_B = get_shmem_info(1); /*get the config parameter */ INIT_CONFIG_PARAMETER(TRACE_CONFIG_FILE, "LOG_DIR=", LOG_DIR, 1); INIT_CONFIG_PARAMETER(TRACE_CONFIG_FILE, "END_FILE=", END_FILE, 1); INIT_CONFIG_PARAMETER(TRACE_CONFIG_FILE, "CIRCLE_TRACE=", CIRCLE_TRACE, 0); INIT_CONFIG_PARAMETER(TRACE_CONFIG_FILE, "MAX_FILE_NUMBER=", MAX_FILE_NUMBER, 0); /*init the buffer size from config file */ INIT_CONFIG_PARAMETER(TRACE_CONFIG_FILE, "CONTENT_SIZE=", SHME_CONTENT_SIZE, 0); SHME_CONTENT_SIZE = SHME_CONTENT_SIZE << 20; /*prepare the log file */ if (shmem_info_A->shm != (char *)(-1)) { /*mk the dir if not exsits,493==0755 */ mkdir(LOG_DIR, 493); /*ini the size, real_size and pid */ //*(shmem_info_A->size) = 0; //*(shmem_info_A->real_size) = 0; *(shmem_info_A->pid) = plat_getpid(); //*(shmem_info_B->size) = 0; //*(shmem_info_B->real_size) = 0; *(shmem_info_B->pid) = plat_getpid(); //buffer=malloc(SHME_MAX_SIZE*sizeof(char)); /*open all files here */ int i = 0; files = plat_alloc(sizeof(struct file_dump) * MAX_FILE_NUMBER); for (i = 0; i < MAX_FILE_NUMBER; i++) { sprintf(files[i].file_name, "%s/%d", LOG_DIR, i); files[i].fp = fopen(files[i].file_name, "wb+"); } /*catch the int and term */ signal(SIGINT, interrupt); signal(SIGTERM, interrupt); /*set the handler of two singnal */ act_full_A.sa_sigaction = collector_A; act_full_A.sa_flags = SA_SIGINFO; act_full_B.sa_sigaction = collector_B; act_full_B.sa_flags = SA_SIGINFO; sigaction(SHME_FULL_A, &act_full_A, NULL); sigaction(SHME_FULL_B, &act_full_B, NULL); /*only the SHME_FULL_A and SHME_FULL_B would be catched */ sigemptyset(&mask); sigaddset(&mask, SHME_FULL_A); sigaddset(&mask, SHME_FULL_B); sigprocmask(SIG_BLOCK, &mask, &oldmask); /*block to wait the two singals */ while (1) { sigsuspend(&oldmask); } interrupt(); } return 0; }
int become_daemon(int dont_fork, int close_all_files, const char *user, const char *input, const char *output, const char *error, const char *access, int *access_fd, FILE **access_fp) { fflush(NULL); // open the files before forking int input_fd = -1, output_fd = -1, error_fd = -1, dev_null; if(input && *input) { if((input_fd = open(input, O_RDONLY, 0666)) == -1) { error("Cannot open input file '%s'.", input); return -1; } } if(output && *output) { if((output_fd = open(output, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { error("Cannot open output log file '%s'", output); if(input_fd != -1) close(input_fd); return -1; } } if(error && *error) { if((error_fd = open(error, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { error("Cannot open error log file '%s'.", error); if(input_fd != -1) close(input_fd); if(output_fd != -1) close(output_fd); return -1; } } if(access && *access && access_fd) { if((*access_fd = open(access, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { error("Cannot open access log file '%s'", access); if(input_fd != -1) close(input_fd); if(output_fd != -1) close(output_fd); if(error_fd != -1) close(error_fd); return -1; } if(access_fp) { *access_fp = fdopen(*access_fd, "w"); if(!*access_fp) { error("Cannot migrate file's '%s' fd %d.", access, *access_fd); if(input_fd != -1) close(input_fd); if(output_fd != -1) close(output_fd); if(error_fd != -1) close(error_fd); close(*access_fd); *access_fd = -1; return -1; } } } if((dev_null = open("/dev/null", O_RDWR, 0666)) == -1) { perror("Cannot open /dev/null"); if(input_fd != -1) close(input_fd); if(output_fd != -1) close(output_fd); if(error_fd != -1) close(error_fd); if(access && access_fd && *access_fd != -1) { close(*access_fd); *access_fd = -1; if(access_fp) { fclose(*access_fp); *access_fp = NULL; } } return -1; } // all files opened // lets do it if(!dont_fork) { int i = fork(); if(i == -1) { perror("cannot fork"); exit(1); } if(i != 0) { exit(0); // the parent } // become session leader if (setsid() < 0) { perror("Cannot become session leader."); exit(2); } } // fork() again if(!dont_fork) { int i = fork(); if(i == -1) { perror("cannot fork"); exit(1); } if(i != 0) { exit(0); // the parent } } // Set new file permissions umask(0); // close all files if(close_all_files) { int i; for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--) if( ((access_fd && i != *access_fd) || !access_fd) && i != dev_null && i != input_fd && i != output_fd && i != error_fd && fd_is_valid(i) ) close(i); } else { close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); } // put the opened files // to our standard file descriptors if(input_fd != -1) { if(input_fd != STDIN_FILENO) { dup2(input_fd, STDIN_FILENO); close(input_fd); } input_fd = -1; } else dup2(dev_null, STDIN_FILENO); if(output_fd != -1) { if(output_fd != STDOUT_FILENO) { dup2(output_fd, STDOUT_FILENO); close(output_fd); } output_fd = -1; } else dup2(dev_null, STDOUT_FILENO); if(error_fd != -1) { if(error_fd != STDERR_FILENO) { dup2(error_fd, STDERR_FILENO); close(error_fd); } error_fd = -1; } else dup2(dev_null, STDERR_FILENO); // close /dev/null if(dev_null != STDIN_FILENO && dev_null != STDOUT_FILENO && dev_null != STDERR_FILENO) close(dev_null); // generate our pid file if(pidfile[0]) { pidfd = open(pidfile, O_RDWR | O_CREAT, 0644); if(pidfd >= 0) { if(ftruncate(pidfd, 0) != 0) error("Cannot truncate pidfile '%s'.", pidfile); char b[100]; sprintf(b, "%d\n", getpid()); ssize_t i = write(pidfd, b, strlen(b)); if(i <= 0) error("Cannot write pidfile '%s'.", pidfile); // don't close it, we might need it at exit // close(pidfd); } else error("Failed to open pidfile '%s'.", pidfile); } if(user && *user) { if(become_user(user) != 0) { error("Cannot become user '%s'. Continuing as we are.", user); } else info("Successfully became user '%s'.", user); } else if(pidfd != -1) close(pidfd); return(0); }
static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, enum file_close_type close_type) { struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx); struct share_mode_lock *lck = NULL; bool delete_dir = False; NTSTATUS status = NT_STATUS_OK; NTSTATUS status1 = NT_STATUS_OK; const struct security_token *del_nt_token = NULL; const struct security_unix_token *del_token = NULL; NTSTATUS notify_status; if (fsp->conn->sconn->using_smb2) { notify_status = STATUS_NOTIFY_CLEANUP; } else { notify_status = NT_STATUS_OK; } /* * NT can set delete_on_close of the last open * reference to a directory also. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0, ("close_directory: Could not get share mode lock for " "%s\n", fsp_str_dbg(fsp))); return NT_STATUS_INVALID_PARAMETER; } if (fsp->initial_delete_on_close) { bool became_user = False; /* Initial delete on close was set - for * directories we don't care if anyone else * wrote a real delete on close. */ if (get_current_vuid(fsp->conn) != fsp->vuid) { become_user(fsp->conn, fsp->vuid); became_user = True; } send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx, fsp->fsp_name->base_name); set_delete_on_close_lck(fsp, lck, get_current_nttok(fsp->conn), get_current_utok(fsp->conn)); fsp->delete_on_close = true; if (became_user) { unbecome_user(); } } delete_dir = get_delete_on_close_token(lck, fsp->name_hash, &del_nt_token, &del_token); if (delete_dir) { int i; /* See if others still have the dir open. If this is the * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->data->num_share_modes; i++) { struct share_mode_entry *e = &lck->data->share_modes[i]; if (is_valid_share_mode_entry(e) && e->name_hash == fsp->name_hash) { if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } if (serverid_equal(&self, &e->pid) && (e->share_file_id == fsp->fh->gen_id)) { continue; } if (share_mode_stale_pid(lck->data, i)) { continue; } delete_dir = False; break; } } } if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) && delete_dir) { /* Become the user who requested the delete. */ if (!push_sec_ctx()) { smb_panic("close_directory: failed to push sec_ctx.\n"); } set_sec_ctx(del_token->uid, del_token->gid, del_token->ngroups, del_token->groups, del_nt_token); if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_directory: Could not delete share entry for " "%s\n", fsp_str_dbg(fsp))); } TALLOC_FREE(lck); if ((fsp->conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(fsp->conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); return status; } } status = rmdir_internals(talloc_tos(), fsp); DEBUG(5,("close_directory: %s. Delete on close was set - " "deleting directory returned %s.\n", fsp_str_dbg(fsp), nt_errstr(status))); /* unbecome user. */ pop_sec_ctx(); /* * Ensure we remove any change notify requests that would * now fail as the directory has been deleted. */ if (NT_STATUS_IS_OK(status)) { notify_status = NT_STATUS_DELETE_PENDING; } } else { if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_directory: Could not delete share entry for " "%s\n", fsp_str_dbg(fsp))); } TALLOC_FREE(lck); } remove_pending_change_notify_requests_by_fid(fsp, notify_status); status1 = fd_close(fsp); if (!NT_STATUS_IS_OK(status1)) { DEBUG(0, ("Could not close dir! fname=%s, fd=%d, err=%d=%s\n", fsp_str_dbg(fsp), fsp->fh->fd, errno, strerror(errno))); } /* * Do the code common to files and directories. */ close_filestruct(fsp); file_free(req, fsp); if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(status1)) { status = status1; } return status; }
int main(int argc, const char *argv[]) { int ret; int kerr; int opt; int debug_fd = -1; poptContext pc; TALLOC_CTX *main_ctx = NULL; uint8_t *buf = NULL; ssize_t len = 0; const char *ccname = NULL; time_t expire_time = 0; struct input_buffer *ibuf = NULL; struct response *resp = NULL; ssize_t written; struct poptOption long_options[] = { POPT_AUTOHELP {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0, _("Debug level"), NULL}, {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0, _("Add debug timestamps"), NULL}, {"debug-microseconds", 0, POPT_ARG_INT, &debug_microseconds, 0, _("Show timestamps with microseconds"), NULL}, {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0, _("An open file descriptor for the debug logs"), NULL}, {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN, &debug_to_stderr, 0, \ _("Send the debug output to stderr directly."), NULL }, \ POPT_TABLEEND }; /* Set debug level to invalid value so we can decide if -d 0 was used. */ debug_level = SSSDBG_INVALID; pc = poptGetContext(argv[0], argc, argv, long_options, 0); while((opt = poptGetNextOpt(pc)) != -1) { switch(opt) { default: fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), poptStrerror(opt)); poptPrintUsage(pc, stderr, 0); _exit(-1); } } poptFreeContext(pc); DEBUG_INIT(debug_level); debug_prg_name = talloc_asprintf(NULL, "[sssd[ldap_child[%d]]]", getpid()); if (!debug_prg_name) { debug_prg_name = "[sssd[ldap_child]]"; DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n"); goto fail; } if (debug_fd != -1) { ret = set_debug_file_from_fd(debug_fd); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n"); } } DEBUG(SSSDBG_TRACE_FUNC, "ldap_child started.\n"); main_ctx = talloc_new(NULL); if (main_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n"); talloc_free(discard_const(debug_prg_name)); goto fail; } talloc_steal(main_ctx, debug_prg_name); buf = talloc_size(main_ctx, sizeof(uint8_t)*IN_BUF_SIZE); if (buf == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n"); goto fail; } ibuf = talloc_zero(main_ctx, struct input_buffer); if (ibuf == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n"); goto fail; } DEBUG(SSSDBG_TRACE_INTERNAL, "context initialized\n"); errno = 0; len = sss_atomic_read_s(STDIN_FILENO, buf, IN_BUF_SIZE); if (len == -1) { ret = errno; DEBUG(SSSDBG_CRIT_FAILURE, "read failed [%d][%s].\n", ret, strerror(ret)); goto fail; } close(STDIN_FILENO); ret = unpack_buffer(buf, len, ibuf); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "unpack_buffer failed.[%d][%s].\n", ret, strerror(ret)); goto fail; } kerr = privileged_krb5_setup(ibuf); if (kerr != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Privileged Krb5 setup failed.\n"); goto fail; } DEBUG(SSSDBG_TRACE_INTERNAL, "Kerberos context initialized\n"); kerr = become_user(ibuf->uid, ibuf->gid); if (kerr != 0) { DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n"); goto fail; } DEBUG(SSSDBG_TRACE_INTERNAL, "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid()); DEBUG(SSSDBG_TRACE_INTERNAL, "getting TGT sync\n"); kerr = ldap_child_get_tgt_sync(main_ctx, ibuf->context, ibuf->realm_str, ibuf->princ_str, ibuf->keytab_name, ibuf->lifetime, &ccname, &expire_time); if (kerr != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "ldap_child_get_tgt_sync failed.\n"); /* Do not return, must report failure */ } ret = prepare_response(main_ctx, ccname, expire_time, kerr, &resp); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "prepare_response failed. [%d][%s].\n", ret, strerror(ret)); goto fail; } errno = 0; written = sss_atomic_write_s(STDOUT_FILENO, resp->buf, resp->size); if (written == -1) { ret = errno; DEBUG(SSSDBG_CRIT_FAILURE, "write failed [%d][%s].\n", ret, strerror(ret)); goto fail; } if (written != resp->size) { DEBUG(SSSDBG_CRIT_FAILURE, "Expected to write %zu bytes, wrote %zu\n", resp->size, written); goto fail; } DEBUG(SSSDBG_TRACE_FUNC, "ldap_child completed successfully\n"); close(STDOUT_FILENO); talloc_free(main_ctx); _exit(0); fail: DEBUG(SSSDBG_CRIT_FAILURE, "ldap_child failed!\n"); close(STDOUT_FILENO); talloc_free(main_ctx); _exit(-1); }
static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; struct server_id self = messaging_server_id(conn->sconn->msg_ctx); bool delete_file = false; bool changed_user = false; struct share_mode_lock *lck = NULL; NTSTATUS status = NT_STATUS_OK; NTSTATUS tmp_status; struct file_id id; const struct security_unix_token *del_token = NULL; const struct security_token *del_nt_token = NULL; bool got_tokens = false; bool normal_close; int ret_flock, retries = 1; /* Ensure any pending write time updates are done. */ if (fsp->update_write_time_event) { update_write_time_handler(fsp->conn->sconn->ev_ctx, fsp->update_write_time_event, timeval_current(), (void *)fsp); } /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " "lock for file %s\n", fsp_str_dbg(fsp))); return NT_STATUS_INVALID_PARAMETER; } if (fsp->write_time_forced) { DEBUG(10,("close_remove_share_mode: write time forced " "for file %s\n", fsp_str_dbg(fsp))); set_close_write_time(fsp, lck->data->changed_write_time); } else if (fsp->update_write_time_on_close) { /* Someone had a pending write. */ if (null_timespec(fsp->close_write_time)) { DEBUG(10,("close_remove_share_mode: update to current time " "for file %s\n", fsp_str_dbg(fsp))); /* Update to current time due to "normal" write. */ set_close_write_time(fsp, timespec_current()); } else { DEBUG(10,("close_remove_share_mode: write time pending " "for file %s\n", fsp_str_dbg(fsp))); /* Update to time set on close call. */ set_close_write_time(fsp, fsp->close_write_time); } } if (fsp->initial_delete_on_close && !is_delete_on_close_set(lck, fsp->name_hash)) { bool became_user = False; /* Initial delete on close was set and no one else * wrote a real delete on close. */ if (get_current_vuid(conn) != fsp->vuid) { become_user(conn, fsp->vuid); became_user = True; } fsp->delete_on_close = true; set_delete_on_close_lck(fsp, lck, get_current_nttok(conn), get_current_utok(conn)); if (became_user) { unbecome_user(); } } delete_file = is_delete_on_close_set(lck, fsp->name_hash); if (delete_file) { int i; /* See if others still have the file open via this pathname. If this is the case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->data->num_share_modes; i++) { struct share_mode_entry *e = &lck->data->share_modes[i]; if (!is_valid_share_mode_entry(e)) { continue; } if (e->name_hash != fsp->name_hash) { continue; } if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } if (serverid_equal(&self, &e->pid) && (e->share_file_id == fsp->fh->gen_id)) { continue; } if (share_mode_stale_pid(lck->data, i)) { continue; } delete_file = False; break; } } /* * NT can set delete_on_close of the last open * reference to a file. */ normal_close = (close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE); if (!normal_close || !delete_file) { status = NT_STATUS_OK; goto done; } /* * Ok, we have to delete the file */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp_str_dbg(fsp))); /* * Don't try to update the write time when we delete the file */ fsp->update_write_time_on_close = false; got_tokens = get_delete_on_close_token(lck, fsp->name_hash, &del_nt_token, &del_token); SMB_ASSERT(got_tokens); if (!unix_token_equal(del_token, get_current_utok(conn))) { /* Become the user who requested the delete. */ DEBUG(5,("close_remove_share_mode: file %s. " "Change user to uid %u\n", fsp_str_dbg(fsp), (unsigned int)del_token->uid)); if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } set_sec_ctx(del_token->uid, del_token->gid, del_token->ngroups, del_token->groups, del_nt_token); changed_user = true; } /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ tmp_status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(tmp_status)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and stat failed with error %s\n", fsp_str_dbg(fsp), nt_errstr(tmp_status))); /* * Don't save the errno here, we ignore this error */ goto done; } id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); if (!file_id_equal(&fsp->file_id, &id)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and dev and/or inode does not match\n", fsp_str_dbg(fsp))); DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, " "stat file_id %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), file_id_string_tos(&id))); /* * Don't save the errno here, we ignore this error */ goto done; } if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); goto done; } } retry_delete: /* temporary files with delete on close set will not be deleted on a * cifs share using a netapp backend since they are opened with * read + write access mask. * close the file to allow the delete. */ if (fsp->can_write && !S_ISDIR(fsp->fsp_name->st.st_ex_mode) && fsp->fh->ref_count == 1 && retries) { status = fd_close(fsp); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("close_remove_share_mode: Error %s closing %s\n", nt_errstr(status), smb_fname_str_dbg(fsp->fsp_name))); goto skip_retry; } if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); retries = 0; goto retry_delete; } } else { if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); } } /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode * entry - ensure we only delete once by resetting * the delete on close flag. JRA. */ skip_retry: fsp->delete_on_close = false; reset_delete_on_close_lck(fsp, lck); done: if (changed_user) { /* unbecome user. */ pop_sec_ctx(); } /* remove filesystem sharemodes */ ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, 0, 0); if (ret_flock == -1) { DEBUG(2, ("close_remove_share_mode: removing kernel flock for " "%s failed: %s\n", fsp_str_dbg(fsp), strerror(errno))); } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share " "entry for file %s\n", fsp_str_dbg(fsp))); } TALLOC_FREE(lck); if (delete_file) { /* * Do the notification after we released the share * mode lock. Inside notify_fname we take out another * tdb lock. With ctdb also accessing our databases, * this can lead to deadlocks. Putting this notify * after the TALLOC_FREE(lck) above we avoid locking * two records simultaneously. Notifies are async and * informational only, so calling the notify_fname * without holding the share mode lock should not do * any harm. */ notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, fsp->fsp_name->base_name); } return status; }
int main(int argc, char *argv[]) { const char *prog = xbasename(argv[0]); int i, cmd = 0, checking_flag = 0; printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); /* if no configuration file is specified on the command line, use default */ configfile = xmalloc(SMALLBUF); snprintf(configfile, SMALLBUF, "%s/upsmon.conf", confpath()); configfile = xrealloc(configfile, strlen(configfile) + 1); run_as_user = xstrdup(RUN_AS_USER); while ((i = getopt(argc, argv, "+Dhic:f:pu:VK46")) != -1) { switch (i) { case 'c': if (!strncmp(optarg, "fsd", strlen(optarg))) cmd = SIGCMD_FSD; if (!strncmp(optarg, "stop", strlen(optarg))) cmd = SIGCMD_STOP; if (!strncmp(optarg, "reload", strlen(optarg))) cmd = SIGCMD_RELOAD; /* bad command name given */ if (cmd == 0) help(argv[0]); break; case 'D': nut_debug_level++; break; case 'f': free(configfile); configfile = xstrdup(optarg); break; case 'h': help(argv[0]); break; case 'K': checking_flag = 1; break; case 'p': use_pipe = 0; break; case 'u': free(run_as_user); run_as_user = xstrdup(optarg); break; case 'V': /* just show the banner */ exit(EXIT_SUCCESS); case '4': opt_af = AF_INET; break; case '6': opt_af = AF_INET6; break; default: help(argv[0]); break; } } if (cmd) { sendsignal(prog, cmd); exit(EXIT_SUCCESS); } /* otherwise, we are being asked to start. * so check if a previous instance is running by sending signal '0' * (Ie 'kill <pid> 0') */ if (sendsignal(prog, 0) == 0) { printf("Fatal error: A previous upsmon instance is already running!\n"); printf("Either stop the previous instance first, or use the 'reload' command.\n"); exit(EXIT_FAILURE); } argc -= optind; argv += optind; open_syslog(prog); loadconfig(); if (checking_flag) exit(check_pdflag()); if (shutdowncmd == NULL) printf("Warning: no shutdown command defined!\n"); /* we may need to get rid of a flag from a previous shutdown */ if (powerdownflag != NULL) clear_pdflag(); /* FIXME (else): POWERDOWNFLAG is not defined!! * => fallback to a default value */ if (totalpv < minsupplies) { printf("\nFatal error: insufficient power configured!\n\n"); printf("Sum of power values........: %d\n", totalpv); printf("Minimum value (MINSUPPLIES): %d\n", minsupplies); printf("\nEdit your upsmon.conf and change the values.\n"); exit(EXIT_FAILURE); } if (nut_debug_level < 1) { background(); } else { upsdebugx(1, "debug level is '%d'", nut_debug_level); } /* only do the pipe stuff if the user hasn't disabled it */ if (use_pipe) { struct passwd *new_uid = get_user_pwent(run_as_user); /* === root parent and unprivileged child split here === */ start_pipe(); /* write the pid file now, as we will soon lose root */ writepid(prog); become_user(new_uid); } else { upslogx(LOG_INFO, "Warning: running as one big root process by request (upsmon -p)"); writepid(prog); } /* prep our signal handlers */ setup_signals(); /* reopen the log for the child process */ closelog(); open_syslog(prog); while (exit_flag == 0) { utype_t *ups; /* check flags from signal handlers */ if (userfsd) forceshutdown(); if (reload_flag) reload_conf(); for (ups = firstups; ups != NULL; ups = ups->next) pollups(ups); recalc(); /* make sure the parent hasn't died */ if (use_pipe) check_parent(); /* reap children that have exited */ waitpid(-1, NULL, WNOHANG); sleep(sleepval); } upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); upsmon_cleanup(); exit(EXIT_SUCCESS); }