static NTSTATUS authsam_check_password_internals(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, const struct auth_usersupplied_info *user_info, struct auth_user_info_dc **user_info_dc, bool *authoritative) { NTSTATUS nt_status; const char *account_name = user_info->mapped.account_name; struct ldb_message *msg; struct ldb_dn *domain_dn; DATA_BLOB user_sess_key, lm_sess_key; TALLOC_CTX *tmp_ctx; const char *p = NULL; if (ctx->auth_ctx->sam_ctx == NULL) { DEBUG(0, ("No SAM available, cannot log in users\n")); return NT_STATUS_INVALID_SYSTEM_SERVICE; } if (!account_name || !*account_name) { /* 'not for me' */ return NT_STATUS_NOT_IMPLEMENTED; } tmp_ctx = talloc_new(mem_ctx); if (!tmp_ctx) { return NT_STATUS_NO_MEMORY; } domain_dn = ldb_get_default_basedn(ctx->auth_ctx->sam_ctx); if (domain_dn == NULL) { talloc_free(tmp_ctx); return NT_STATUS_NO_SUCH_DOMAIN; } p = strchr_m(account_name, '@'); if (p != NULL) { const char *nt4_domain = NULL; const char *nt4_account = NULL; bool is_my_domain = false; nt_status = crack_name_to_nt4_name(mem_ctx, ctx->auth_ctx->sam_ctx, /* * DRSUAPI_DS_NAME_FORMAT_UPN_FOR_LOGON ? */ DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, account_name, &nt4_domain, &nt4_account); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return NT_STATUS_NO_SUCH_USER; } is_my_domain = lpcfg_is_mydomain(ctx->auth_ctx->lp_ctx, nt4_domain); if (!is_my_domain) { /* * This is a user within our forest, * but in a different domain, * we're not authoritative */ talloc_free(tmp_ctx); return NT_STATUS_NOT_IMPLEMENTED; } /* * Let's use the NT4 account name for the lookup. */ account_name = nt4_account; } nt_status = authsam_search_account(tmp_ctx, ctx->auth_ctx->sam_ctx, account_name, domain_dn, &msg); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return nt_status; } nt_status = authsam_authenticate(ctx->auth_ctx, tmp_ctx, ctx->auth_ctx->sam_ctx, domain_dn, msg, user_info, &user_sess_key, &lm_sess_key, authoritative); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return nt_status; } nt_status = authsam_make_user_info_dc(tmp_ctx, ctx->auth_ctx->sam_ctx, lpcfg_netbios_name(ctx->auth_ctx->lp_ctx), lpcfg_sam_name(ctx->auth_ctx->lp_ctx), lpcfg_sam_dnsname(ctx->auth_ctx->lp_ctx), domain_dn, msg, user_sess_key, lm_sess_key, user_info_dc); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return nt_status; } talloc_steal(mem_ctx, *user_info_dc); talloc_free(tmp_ctx); return NT_STATUS_OK; }
static WERROR dcesrv_spoolss_parse_printer_name(TALLOC_CTX *mem_ctx, const char *name, const char **_server_name, const char **_object_name, enum ntptr_HandleType *_object_type) { char *p; char *server = NULL; char *server_unc = NULL; const char *object = name; /* no printername is there it's like open server */ if (!name) { *_server_name = NULL; *_object_name = NULL; *_object_type = NTPTR_HANDLE_SERVER; return WERR_OK; } /* just "\\" is invalid */ if (strequal("\\\\", name)) { return WERR_INVALID_PRINTER_NAME; } if (strncmp("\\\\", name, 2) == 0) { server_unc = talloc_strdup(mem_ctx, name); W_ERROR_HAVE_NO_MEMORY(server_unc); server = server_unc + 2; /* here we know we have "\\" in front not followed * by '\0', now see if we have another "\" in the string */ p = strchr_m(server, '\\'); if (!p) { /* there's no other "\", so it's ("\\%s",server) */ *_server_name = server_unc; *_object_name = NULL; *_object_type = NTPTR_HANDLE_SERVER; return WERR_OK; } /* here we know that we have ("\\%s\",server), * if we have '\0' as next then it's an invalid name * otherwise the printer_name */ p[0] = '\0'; /* everything that follows is the printer name */ p++; object = p; /* just "" as server is invalid */ if (strequal(server, "")) { return WERR_INVALID_PRINTER_NAME; } } /* just "" is invalid */ if (strequal(object, "")) { return WERR_INVALID_PRINTER_NAME; } #define XCV_PORT ",XcvPort " #define XCV_MONITOR ",XcvMonitor " if (strncmp(object, XCV_PORT, strlen(XCV_PORT)) == 0) { object += strlen(XCV_PORT); /* just "" is invalid */ if (strequal(object, "")) { return WERR_INVALID_PRINTER_NAME; } *_server_name = server_unc; *_object_name = object; *_object_type = NTPTR_HANDLE_PORT; return WERR_OK; } else if (strncmp(object, XCV_MONITOR, strlen(XCV_MONITOR)) == 0) { object += strlen(XCV_MONITOR); /* just "" is invalid */ if (strequal(object, "")) { return WERR_INVALID_PRINTER_NAME; } *_server_name = server_unc; *_object_name = object; *_object_type = NTPTR_HANDLE_MONITOR; return WERR_OK; } *_server_name = server_unc; *_object_name = object; *_object_type = NTPTR_HANDLE_PRINTER; return WERR_OK; }
/** interpret a single element from a interfaces= config line This handles the following different forms: 1) wildcard interface name 2) DNS name 3) IP/masklen 4) ip/mask 5) bcast/mask **/ static void interpret_interface(TALLOC_CTX *mem_ctx, const char *token, struct iface_struct *probed_ifaces, int total_probed, struct interface **local_interfaces) { struct in_addr ip, nmask; char *p; char *address; int i, added=0; ip.s_addr = 0; nmask.s_addr = 0; /* first check if it is an interface name */ for (i=0;i<total_probed;i++) { if (gen_fnmatch(token, probed_ifaces[i].name) == 0) { add_interface(mem_ctx, probed_ifaces[i].ip, probed_ifaces[i].netmask, local_interfaces); added = 1; } } if (added) return; /* maybe it is a DNS name */ p = strchr_m(token,'/'); if (!p) { /* don't try to do dns lookups on wildcard names */ if (strpbrk(token, "*?") != NULL) { return; } ip.s_addr = interpret_addr2(token).s_addr; for (i=0;i<total_probed;i++) { if (ip.s_addr == probed_ifaces[i].ip.s_addr) { add_interface(mem_ctx, probed_ifaces[i].ip, probed_ifaces[i].netmask, local_interfaces); return; } } DEBUG(2,("can't determine netmask for %s\n", token)); return; } address = talloc_strdup(mem_ctx, token); p = strchr_m(address,'/'); /* parse it into an IP address/netmasklength pair */ *p++ = 0; ip.s_addr = interpret_addr2(address).s_addr; if (strlen(p) > 2) { nmask.s_addr = interpret_addr2(p).s_addr; } else { nmask.s_addr = htonl(((ALLONES >> atoi(p)) ^ ALLONES)); } /* maybe the first component was a broadcast address */ if (ip.s_addr == MKBCADDR(ip.s_addr, nmask.s_addr) || ip.s_addr == MKNETADDR(ip.s_addr, nmask.s_addr)) { for (i=0;i<total_probed;i++) { if (same_net_v4(ip, probed_ifaces[i].ip, nmask)) { add_interface(mem_ctx, probed_ifaces[i].ip, nmask, local_interfaces); talloc_free(address); return; } } DEBUG(2,("Can't determine ip for broadcast address %s\n", address)); talloc_free(address); return; } add_interface(mem_ctx, ip, nmask, local_interfaces); talloc_free(address); }
char *alloc_sub_basic(const char *smb_name, const char *str) { char *b, *p, *s, *r, *a_string; fstring pidstr; struct passwd *pass; const char *local_machine_name = get_local_machine_name(); /* workaround to prevent a crash while looking at bug #687 */ if (!str) { DEBUG(0,("alloc_sub_basic: NULL source string! This should not happen\n")); return NULL; } a_string = SMB_STRDUP(str); if (a_string == NULL) { DEBUG(0, ("alloc_sub_specified: Out of memory!\n")); return NULL; } for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) { r = NULL; b = a_string; switch (*(p+1)) { case 'U' : r = strdup_lower(smb_name); if (r == NULL) { goto error; } a_string = realloc_string_sub(a_string, "%U", r); break; case 'G' : r = SMB_STRDUP(smb_name); if (r == NULL) { goto error; } if ((pass = Get_Pwnam(r))!=NULL) { a_string = realloc_string_sub(a_string, "%G", gidtoname(pass->pw_gid)); } break; case 'D' : r = strdup_upper(current_user_info.domain); if (r == NULL) { goto error; } a_string = realloc_string_sub(a_string, "%D", r); break; case 'I' : a_string = realloc_string_sub(a_string, "%I", client_addr()); break; case 'i': a_string = realloc_string_sub( a_string, "%i", client_socket_addr() ); break; case 'L' : if ( StrnCaseCmp(p, "%LOGONSERVER%", strlen("%LOGONSERVER%")) == 0 ) { break; } if (local_machine_name && *local_machine_name) { a_string = realloc_string_sub(a_string, "%L", local_machine_name); } else { a_string = realloc_string_sub(a_string, "%L", global_myname()); } break; case 'N': a_string = realloc_string_sub(a_string, "%N", automount_server(smb_name)); break; case 'M' : a_string = realloc_string_sub(a_string, "%M", client_name()); break; case 'R' : a_string = realloc_string_sub(a_string, "%R", remote_proto); break; case 'T' : a_string = realloc_string_sub(a_string, "%T", timestring(False)); break; case 'a' : a_string = realloc_string_sub(a_string, "%a", remote_arch); break; case 'd' : slprintf(pidstr,sizeof(pidstr)-1, "%d",(int)sys_getpid()); a_string = realloc_string_sub(a_string, "%d", pidstr); break; case 'h' : a_string = realloc_string_sub(a_string, "%h", myhostname()); break; case 'm' : a_string = realloc_string_sub(a_string, "%m", remote_machine); break; case 'v' : a_string = realloc_string_sub(a_string, "%v", SAMBA_VERSION_STRING); break; case 'w' : a_string = realloc_string_sub(a_string, "%w", lp_winbind_separator()); break; case '$' : a_string = realloc_expand_env_var(a_string, p); /* Expand environment variables */ break; case '(': a_string = realloc_expand_longvar( a_string, p ); break; default: break; } p++; SAFE_FREE(r); if ( !a_string ) { return NULL; } } return a_string; error: SAFE_FREE(a_string); return NULL; }
char *alloc_sub_advanced(int snum, const char *user, const char *connectpath, gid_t gid, const char *smb_name, const char *str) { char *a_string, *ret_string; char *b, *p, *s, *h; a_string = SMB_STRDUP(str); if (a_string == NULL) { DEBUG(0, ("alloc_sub_advanced: Out of memory!\n")); return NULL; } for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) { b = a_string; switch (*(p+1)) { case 'N' : a_string = realloc_string_sub(a_string, "%N", automount_server(user)); break; case 'H': if ((h = get_user_home_dir(user))) a_string = realloc_string_sub(a_string, "%H", h); break; case 'P': a_string = realloc_string_sub(a_string, "%P", connectpath); break; case 'S': a_string = realloc_string_sub(a_string, "%S", lp_servicename(snum)); break; case 'g': a_string = realloc_string_sub(a_string, "%g", gidtoname(gid)); break; case 'u': a_string = realloc_string_sub(a_string, "%u", user); break; /* Patch from [email protected] Left the %N (NIS * server name) in standard_sub_basic as it is * a feature for logon servers, hence uses the * username. The %p (NIS server path) code is * here as it is used instead of the default * "path =" string in [homes] and so needs the * service name, not the username. */ case 'p': a_string = realloc_string_sub(a_string, "%p", automount_path(lp_servicename(snum))); break; default: break; } p++; if (a_string == NULL) { return NULL; } } ret_string = alloc_sub_basic(smb_name, a_string); SAFE_FREE(a_string); return ret_string; }
NTSTATUS unix_convert(TALLOC_CTX *ctx, connection_struct *conn, const char *orig_path, bool allow_wcard_last_component, char **pp_conv_path, char **pp_saved_last_component, SMB_STRUCT_STAT *pst) { SMB_STRUCT_STAT st; char *start, *end; char *dirpath = NULL; char *name = NULL; char *stream = NULL; bool component_was_mangled = False; bool name_has_wildcard = False; bool posix_pathnames = false; NTSTATUS result; int ret = -1; SET_STAT_INVALID(*pst); *pp_conv_path = NULL; if(pp_saved_last_component) { *pp_saved_last_component = NULL; } if (conn->printer) { /* we don't ever use the filenames on a printer share as a filename - so don't convert them */ if (!(*pp_conv_path = talloc_strdup(ctx,orig_path))) { return NT_STATUS_NO_MEMORY; } return NT_STATUS_OK; } DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path)); /* * Conversion to basic unix format is already done in * check_path_syntax(). */ /* * Names must be relative to the root of the service - any leading /. * and trailing /'s should have been trimmed by check_path_syntax(). */ #ifdef DEVELOPER SMB_ASSERT(*orig_path != '/'); #endif /* * If we trimmed down to a single '\0' character * then we should use the "." directory to avoid * searching the cache, but not if we are in a * printing share. * As we know this is valid we can return true here. */ if (!*orig_path) { if (!(name = talloc_strdup(ctx,"."))) { return NT_STATUS_NO_MEMORY; } if (SMB_VFS_STAT(conn,name,&st) == 0) { *pst = st; } else { return map_nt_error_from_unix(errno); } DEBUG(5,("conversion finished \"\" -> %s\n",name)); goto done; } if (orig_path[0] == '.' && (orig_path[1] == '/' || orig_path[1] == '\0')) { /* Start of pathname can't be "." only. */ if (orig_path[1] == '\0' || orig_path[2] == '\0') { result = NT_STATUS_OBJECT_NAME_INVALID; } else { result =determine_path_error( &orig_path[2], allow_wcard_last_component); } return result; } if (!(name = talloc_strdup(ctx, orig_path))) { DEBUG(0, ("talloc_strdup failed\n")); return NT_STATUS_NO_MEMORY; } /* * Large directory fix normalization. If we're case sensitive, and * the case preserving parameters are set to "no", normalize the case of * the incoming filename from the client WHETHER IT EXISTS OR NOT ! * This is in conflict with the current (3.0.20) man page, but is * what people expect from the "large directory howto". I'll update * the man page. Thanks to [email protected] for finding this. JRA. */ if (conn->case_sensitive && !conn->case_preserve && !conn->short_case_preserve) { strnorm(name, lp_defaultcase(SNUM(conn))); } /* * Ensure saved_last_component is valid even if file exists. */ if(pp_saved_last_component) { end = strrchr_m(name, '/'); if (end) { *pp_saved_last_component = talloc_strdup(ctx, end + 1); } else { *pp_saved_last_component = talloc_strdup(ctx, name); } } posix_pathnames = lp_posix_pathnames(); if (!posix_pathnames) { stream = strchr_m(name, ':'); if (stream != NULL) { char *tmp = talloc_strdup(ctx, stream); if (tmp == NULL) { TALLOC_FREE(name); return NT_STATUS_NO_MEMORY; } *stream = '\0'; stream = tmp; } } start = name; /* If we're providing case insentive semantics or * the underlying filesystem is case insensitive, * then a case-normalized hit in the stat-cache is * authoratitive. JRA. */ if((!conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && stat_cache_lookup(conn, &name, &dirpath, &start, &st)) { *pst = st; goto done; } /* * Make sure "dirpath" is an allocated string, we use this for * building the directories with asprintf and free it. */ if ((dirpath == NULL) && (!(dirpath = talloc_strdup(ctx,"")))) { DEBUG(0, ("talloc_strdup failed\n")); TALLOC_FREE(name); return NT_STATUS_NO_MEMORY; } /* * stat the name - if it exists then we are all done! */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn,name,&st); } else { ret = SMB_VFS_STAT(conn,name,&st); } if (ret == 0) { /* Ensure we catch all names with in "/." this is disallowed under Windows. */ const char *p = strstr(name, "/."); /* mb safe. */ if (p) { if (p[2] == '/') { /* Error code within a pathname. */ result = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } else if (p[2] == '\0') { /* Error code at the end of a pathname. */ result = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } } stat_cache_add(orig_path, name, conn->case_sensitive); DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); *pst = st; goto done; } DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", name, dirpath, start)); /* * A special case - if we don't have any mangling chars and are case * sensitive or the underlying filesystem is case insentive then searching * won't help. */ if ((conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && !mangle_is_mangled(name, conn->params)) { goto done; } /* * is_mangled() was changed to look at an entire pathname, not * just a component. JRA. */ if (mangle_is_mangled(start, conn->params)) { component_was_mangled = True; } /* * Now we need to recursively match the name against the real * directory structure. */ /* * Match each part of the path name separately, trying the names * as is first, then trying to scan the directory for matching names. */ for (; start ; start = (end?end+1:(char *)NULL)) { /* * Pinpoint the end of this section of the filename. */ /* mb safe. '/' can't be in any encoded char. */ end = strchr(start, '/'); /* * Chop the name at this point. */ if (end) { *end = 0; } if (pp_saved_last_component) { TALLOC_FREE(*pp_saved_last_component); *pp_saved_last_component = talloc_strdup(ctx, end ? end + 1 : start); if (!*pp_saved_last_component) { DEBUG(0, ("talloc failed\n")); return NT_STATUS_NO_MEMORY; } } /* The name cannot have a component of "." */ if (ISDOT(start)) { if (!end) { /* Error code at the end of a pathname. */ result = NT_STATUS_OBJECT_NAME_INVALID; } else { result = determine_path_error(end+1, allow_wcard_last_component); } goto fail; } /* The name cannot have a wildcard if it's not the last component. */ name_has_wildcard = ms_has_wild(start); /* Wildcard not valid anywhere. */ if (name_has_wildcard && !allow_wcard_last_component) { result = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } /* Wildcards never valid within a pathname. */ if (name_has_wildcard && end) { result = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } /* * Check if the name exists up to this point. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn,name, &st); } else { ret = SMB_VFS_STAT(conn,name, &st); } if (ret == 0) { /* * It exists. it must either be a directory or this must * be the last part of the path for it to be OK. */ if (end && !(st.st_mode & S_IFDIR)) { /* * An intermediate part of the name isn't * a directory. */ DEBUG(5,("Not a dir %s\n",start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. This * is used to return an error of ERRbadpath * rather than ERRbadfile. Some Windows * applications depend on the difference between * these two errors. */ result = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } if (!end) { /* * We just scanned for, and found the end of * the path. We must return the valid stat * struct. JRA. */ *pst = st; } } else { char *found_name = NULL; /* Stat failed - ensure we don't use it. */ SET_STAT_INVALID(st); /* * Reset errno so we can detect * directory open errors. */ errno = 0; /* * Try to find this part of the path in the directory. */ if (name_has_wildcard || (get_real_filename_mangled( conn, dirpath, start, talloc_tos(), &found_name) == -1)) { char *unmangled; if (end) { /* * An intermediate part of the name * can't be found. */ DEBUG(5,("Intermediate not found %s\n", start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. * This is used to return an error of * ERRbadpath rather than ERRbadfile. * Some Windows applications depend on * the difference between these two * errors. */ /* * ENOENT, ENOTDIR and ELOOP all map * to NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP) { result = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { result = map_nt_error_from_unix(errno); } goto fail; } /* ENOENT is the only valid error here. */ if ((errno != 0) && (errno != ENOENT)) { /* * ENOTDIR and ELOOP both map to * NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOTDIR || errno == ELOOP) { result = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { result = map_nt_error_from_unix(errno); } goto fail; } /* * Just the last part of the name doesn't exist. * We need to strupper() or strlower() it as * this conversion may be used for file creation * purposes. Fix inspired by * Thomas Neumann <*****@*****.**>. */ if (!conn->case_preserve || (mangle_is_8_3(start, False, conn->params) && !conn->short_case_preserve)) { strnorm(start, lp_defaultcase(SNUM(conn))); } /* * check on the mangled stack to see if we can * recover the base of the filename. */ if (mangle_is_mangled(start, conn->params) && mangle_lookup_name_from_8_3(ctx, start, &unmangled, conn->params)) { char *tmp; size_t start_ofs = start - name; if (*dirpath != '\0') { tmp = talloc_asprintf(ctx, "%s/%s", dirpath, unmangled); TALLOC_FREE(unmangled); } else { tmp = unmangled; } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); name = tmp; start = name + start_ofs; end = start + strlen(start); } DEBUG(5,("New file %s\n",start)); goto done; } /* * Restore the rest of the string. If the string was * mangled the size may have changed. */ if (end) { char *tmp; size_t start_ofs = start - name; if (*dirpath != '\0') { tmp = talloc_asprintf(ctx, "%s/%s/%s", dirpath, found_name, end+1); } else { tmp = talloc_asprintf(ctx, "%s/%s", found_name, end+1); } if (tmp == NULL) { DEBUG(0, ("talloc_asprintf failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); name = tmp; start = name + start_ofs; end = start + strlen(found_name); *end = '\0'; } else { char *tmp; size_t start_ofs = start - name; if (*dirpath != '\0') { tmp = talloc_asprintf(ctx, "%s/%s", dirpath, found_name); } else { tmp = talloc_strdup(ctx, found_name); } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); name = tmp; start = name + start_ofs; /* * We just scanned for, and found the end of * the path. We must return a valid stat struct * if it exists. JRA. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn,name, &st); } else { ret = SMB_VFS_STAT(conn,name, &st); } if (ret == 0) { *pst = st; } else { SET_STAT_INVALID(st); } } TALLOC_FREE(found_name); } /* end else */ #ifdef DEVELOPER /* * This sucks! * We should never provide different behaviors * depending on DEVELOPER!!! */ if (VALID_STAT(st)) { bool delete_pending; get_file_infos(vfs_file_id_from_sbuf(conn, &st), &delete_pending, NULL); if (delete_pending) { result = NT_STATUS_DELETE_PENDING; goto fail; } } #endif /* * Add to the dirpath that we have resolved so far. */ if (*dirpath != '\0') { char *tmp = talloc_asprintf(ctx, "%s/%s", dirpath, start); if (!tmp) { DEBUG(0, ("talloc_asprintf failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(dirpath); dirpath = tmp; } else { TALLOC_FREE(dirpath); if (!(dirpath = talloc_strdup(ctx,start))) { DEBUG(0, ("talloc_strdup failed\n")); return NT_STATUS_NO_MEMORY; } } /* * Don't cache a name with mangled or wildcard components * as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, dirpath, conn->case_sensitive); } /* * Restore the / that we wiped out earlier. */ if (end) { *end = '/'; } } /* * Don't cache a name with mangled or wildcard components * as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, name, conn->case_sensitive); } /* * The name has been resolved. */ DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); done: if (stream != NULL) { char *tmp = NULL; result = build_stream_path(ctx, conn, orig_path, name, stream, pst, &tmp); if (!NT_STATUS_IS_OK(result)) { goto fail; } DEBUG(10, ("build_stream_path returned %s\n", tmp)); TALLOC_FREE(name); name = tmp; } *pp_conv_path = name; TALLOC_FREE(dirpath); return NT_STATUS_OK; fail: DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start)); if (*dirpath != '\0') { *pp_conv_path = talloc_asprintf(ctx, "%s/%s", dirpath, start); } else { *pp_conv_path = talloc_strdup(ctx, start); } if (!*pp_conv_path) { DEBUG(0, ("talloc_asprintf failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); TALLOC_FREE(dirpath); return result; }
bool sysv_cache_reload(void) { char **lines; int i; #if defined(HPUX) DEBUG(5, ("reloading hpux printcap cache\n")); #else DEBUG(5, ("reloading sysv printcap cache\n")); #endif if ((lines = file_lines_pload("/usr/bin/lpstat -v", NULL)) == NULL) { #if defined(HPUX) /* * if "lpstat -v" is NULL then we check if schedular is running if it is * that means no printers are added on the HP-UX system, if schedular is not * running we display reload error. */ char **scheduler; scheduler = file_lines_pload("/usr/bin/lpstat -r", NULL); if(!strcmp(*scheduler,"scheduler is running")){ DEBUG(3,("No Printers found!!!\n")); TALLOC_FREE(scheduler); return True; } else{ DEBUG(3,("Scheduler is not running!!!\n")); TALLOC_FREE(scheduler); return False; } #else DEBUG(3,("No Printers found!!!\n")); return False; #endif } for (i = 0; lines[i]; i++) { char *name, *tmp; char *buf = lines[i]; /* eat "system/device for " */ if (((tmp = strchr_m(buf, ' ')) == NULL) || ((tmp = strchr_m(++tmp, ' ')) == NULL)) continue; /* * In case we're only at the "for ". */ if(!strncmp("for ", ++tmp, 4)) { tmp=strchr_m(tmp, ' '); tmp++; } /* Eat whitespace. */ while(*tmp == ' ') ++tmp; /* * On HPUX there is an extra line that can be ignored. * d.thibadeau 2001/08/09 */ if(!strncmp("remote to", tmp, 9)) continue; name = tmp; /* truncate the ": ..." */ if ((tmp = strchr_m(name, ':')) != NULL) *tmp = '\0'; /* add it to the cache */ if (!pcap_cache_add(name, NULL, NULL)) { TALLOC_FREE(lines); return False; } } TALLOC_FREE(lines); return True; }
bool map_username(TALLOC_CTX *ctx, const char *user_in, char **p_user_out) { XFILE *f; char *mapfile = lp_username_map(talloc_tos()); char *s; char buf[512]; bool mapped_user = False; char *cmd = lp_username_map_script(talloc_tos()); *p_user_out = NULL; if (!user_in) return false; /* Initially make a copy of the incoming name. */ *p_user_out = talloc_strdup(ctx, user_in); if (!*p_user_out) { return false; } if (strequal(user_in,get_last_to())) return false; if (strequal(user_in,get_last_from())) { DEBUG(3,("Mapped user %s to %s\n",user_in,get_last_to())); TALLOC_FREE(*p_user_out); *p_user_out = talloc_strdup(ctx, get_last_to()); return true; } if (fetch_map_from_gencache(ctx, user_in, p_user_out)) { return true; } /* first try the username map script */ if ( *cmd ) { char **qlines; char *command = NULL; int numlines, ret, fd; command = talloc_asprintf(ctx, "%s \"%s\"", cmd, user_in); if (!command) { return false; } DEBUG(10,("Running [%s]\n", command)); ret = smbrun(command, &fd); DEBUGADD(10,("returned [%d]\n", ret)); TALLOC_FREE(command); if ( ret != 0 ) { if (fd != -1) close(fd); return False; } numlines = 0; qlines = fd_lines_load(fd, &numlines, 0, ctx); DEBUGADD(10,("Lines returned = [%d]\n", numlines)); close(fd); /* should be either no lines or a single line with the mapped username */ if (numlines && qlines) { DEBUG(3,("Mapped user %s to %s\n", user_in, qlines[0] )); set_last_from_to(user_in, qlines[0]); store_map_in_gencache(ctx, user_in, qlines[0]); TALLOC_FREE(*p_user_out); *p_user_out = talloc_strdup(ctx, qlines[0]); if (!*p_user_out) { return false; } } TALLOC_FREE(qlines); return numlines != 0; } /* ok. let's try the mapfile */ if (!*mapfile) return False; f = x_fopen(mapfile,O_RDONLY, 0); if (!f) { DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) )); return False; } DEBUG(4,("Scanning username map %s\n",mapfile)); while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) { char *unixname = s; char *dosname = strchr_m(unixname,'='); char **dosuserlist; bool return_if_mapped = False; if (!dosname) continue; *dosname++ = 0; unixname = skip_space(unixname); if ('!' == *unixname) { return_if_mapped = True; unixname = skip_space(unixname+1); } if (!*unixname || strchr_m("#;",*unixname)) continue; { int l = strlen(unixname); while (l && isspace((int)unixname[l-1])) { unixname[l-1] = 0; l--; } } /* skip lines like 'user = '******'*') || user_in_list(ctx, user_in, (const char * const *)dosuserlist)) { DEBUG(3,("Mapped user %s to %s\n",user_in,unixname)); mapped_user = True; set_last_from_to(user_in, unixname); store_map_in_gencache(ctx, user_in, unixname); TALLOC_FREE(*p_user_out); *p_user_out = talloc_strdup(ctx, unixname); if (!*p_user_out) { TALLOC_FREE(dosuserlist); x_fclose(f); return false; } if ( return_if_mapped ) { TALLOC_FREE(dosuserlist); x_fclose(f); return True; } } TALLOC_FREE(dosuserlist); } x_fclose(f); /* * If we didn't successfully map a user in the loop above, * setup the last_from and last_to as an optimization so * that we don't scan the file again for the same user. */ if (!mapped_user) { DEBUG(8, ("The user '%s' has no mapping. " "Skip it next time.\n", user_in)); set_last_from_to(user_in, user_in); store_map_in_gencache(ctx, user_in, user_in); } return mapped_user; }
bool lookup_name(TALLOC_CTX *mem_ctx, const char *full_name, int flags, const char **ret_domain, const char **ret_name, struct dom_sid *ret_sid, enum lsa_SidType *ret_type) { char *p; const char *tmp; const char *domain = NULL; const char *name = NULL; uint32 rid; struct dom_sid sid; enum lsa_SidType type; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { DEBUG(0, ("talloc_new failed\n")); return false; } p = strchr_m(full_name, '\\'); if (p != NULL) { domain = talloc_strndup(tmp_ctx, full_name, PTR_DIFF(p, full_name)); name = talloc_strdup(tmp_ctx, p+1); } else { domain = talloc_strdup(tmp_ctx, ""); name = talloc_strdup(tmp_ctx, full_name); } if ((domain == NULL) || (name == NULL)) { DEBUG(0, ("talloc failed\n")); TALLOC_FREE(tmp_ctx); return false; } DEBUG(10,("lookup_name: %s => domain=[%s], name=[%s]\n", full_name, domain, name)); DEBUG(10, ("lookup_name: flags = 0x0%x\n", flags)); if ((flags & LOOKUP_NAME_DOMAIN) && strequal(domain, get_global_sam_name())) { /* It's our own domain, lookup the name in passdb */ if (lookup_global_sam_name(name, flags, &rid, &type)) { sid_compose(&sid, get_global_sam_sid(), rid); goto ok; } TALLOC_FREE(tmp_ctx); return false; } if ((flags & LOOKUP_NAME_BUILTIN) && strequal(domain, builtin_domain_name())) { if (strlen(name) == 0) { /* Swap domain and name */ tmp = name; name = domain; domain = tmp; sid_copy(&sid, &global_sid_Builtin); type = SID_NAME_DOMAIN; goto ok; } /* Explicit request for a name in BUILTIN */ if (lookup_builtin_name(name, &rid)) { sid_compose(&sid, &global_sid_Builtin, rid); type = SID_NAME_ALIAS; goto ok; } TALLOC_FREE(tmp_ctx); return false; } /* Try the explicit winbind lookup first, don't let it guess the * domain yet at this point yet. This comes later. */ if ((domain[0] != '\0') && (flags & ~(LOOKUP_NAME_DOMAIN|LOOKUP_NAME_ISOLATED)) && (winbind_lookup_name(domain, name, &sid, &type))) { goto ok; } if (((flags & LOOKUP_NAME_NO_NSS) == 0) && strequal(domain, unix_users_domain_name())) { if (lookup_unix_user_name(name, &sid)) { type = SID_NAME_USER; goto ok; } TALLOC_FREE(tmp_ctx); return false; } if (((flags & LOOKUP_NAME_NO_NSS) == 0) && strequal(domain, unix_groups_domain_name())) { if (lookup_unix_group_name(name, &sid)) { type = SID_NAME_DOM_GRP; goto ok; } TALLOC_FREE(tmp_ctx); return false; } if ((domain[0] == '\0') && (!(flags & LOOKUP_NAME_ISOLATED))) { TALLOC_FREE(tmp_ctx); return false; } /* Now the guesswork begins, we haven't been given an explicit * domain. Try the sequence as documented on * http://msdn.microsoft.com/library/en-us/secmgmt/security/lsalookupnames.asp * November 27, 2005 */ /* 1. well-known names */ if ((flags & LOOKUP_NAME_WKN) && lookup_wellknown_name(tmp_ctx, name, &sid, &domain)) { type = SID_NAME_WKN_GRP; goto ok; } /* 2. Builtin domain as such */ if ((flags & (LOOKUP_NAME_BUILTIN|LOOKUP_NAME_REMOTE)) && strequal(name, builtin_domain_name())) { /* Swap domain and name */ tmp = name; name = domain; domain = tmp; sid_copy(&sid, &global_sid_Builtin); type = SID_NAME_DOMAIN; goto ok; } /* 3. Account domain */ if ((flags & LOOKUP_NAME_DOMAIN) && strequal(name, get_global_sam_name())) { if (!secrets_fetch_domain_sid(name, &sid)) { DEBUG(3, ("Could not fetch my SID\n")); TALLOC_FREE(tmp_ctx); return false; } /* Swap domain and name */ tmp = name; name = domain; domain = tmp; type = SID_NAME_DOMAIN; goto ok; } /* 4. Primary domain */ if ((flags & LOOKUP_NAME_DOMAIN) && !IS_DC && strequal(name, lp_workgroup())) { if (!secrets_fetch_domain_sid(name, &sid)) { DEBUG(3, ("Could not fetch the domain SID\n")); TALLOC_FREE(tmp_ctx); return false; } /* Swap domain and name */ tmp = name; name = domain; domain = tmp; type = SID_NAME_DOMAIN; goto ok; } /* 5. Trusted domains as such, to me it looks as if members don't do this, tested an XP workstation in a NT domain -- vl */ if ((flags & LOOKUP_NAME_REMOTE) && IS_DC && (pdb_get_trusteddom_pw(name, NULL, &sid, NULL))) { /* Swap domain and name */ tmp = name; name = domain; domain = tmp; type = SID_NAME_DOMAIN; goto ok; } /* 6. Builtin aliases */ if ((flags & LOOKUP_NAME_BUILTIN) && lookup_builtin_name(name, &rid)) { domain = talloc_strdup(tmp_ctx, builtin_domain_name()); sid_compose(&sid, &global_sid_Builtin, rid); type = SID_NAME_ALIAS; goto ok; } /* 7. Local systems' SAM (DCs don't have a local SAM) */ /* 8. Primary SAM (On members, this is the domain) */ /* Both cases are done by looking at our passdb */ if ((flags & LOOKUP_NAME_DOMAIN) && lookup_global_sam_name(name, flags, &rid, &type)) { domain = talloc_strdup(tmp_ctx, get_global_sam_name()); sid_compose(&sid, get_global_sam_sid(), rid); goto ok; } /* Now our local possibilities are exhausted. */ if (!(flags & LOOKUP_NAME_REMOTE)) { TALLOC_FREE(tmp_ctx); return false; } /* If we are not a DC, we have to ask in our primary domain. Let * winbind do that. */ if (!IS_DC && (winbind_lookup_name(lp_workgroup(), name, &sid, &type))) { domain = talloc_strdup(tmp_ctx, lp_workgroup()); goto ok; } /* 9. Trusted domains */ /* If we're a DC we have to ask all trusted DC's. Winbind does not do * that (yet), but give it a chance. */ if (IS_DC && winbind_lookup_name("", name, &sid, &type)) { struct dom_sid dom_sid; enum lsa_SidType domain_type; if (type == SID_NAME_DOMAIN) { /* Swap name and type */ tmp = name; name = domain; domain = tmp; goto ok; } /* Here we have to cope with a little deficiency in the * winbind API: We have to ask it again for the name of the * domain it figured out itself. Maybe fix that later... */ sid_copy(&dom_sid, &sid); sid_split_rid(&dom_sid, NULL); if (!winbind_lookup_sid(tmp_ctx, &dom_sid, &domain, NULL, &domain_type) || (domain_type != SID_NAME_DOMAIN)) { DEBUG(2, ("winbind could not find the domain's name " "it just looked up for us\n")); TALLOC_FREE(tmp_ctx); return false; } goto ok; } /* 10. Don't translate */ /* 11. Ok, windows would end here. Samba has two more options: Unmapped users and unmapped groups */ if (((flags & LOOKUP_NAME_NO_NSS) == 0) && lookup_unix_user_name(name, &sid)) { domain = talloc_strdup(tmp_ctx, unix_users_domain_name()); type = SID_NAME_USER; goto ok; } if (((flags & LOOKUP_NAME_NO_NSS) == 0) && lookup_unix_group_name(name, &sid)) { domain = talloc_strdup(tmp_ctx, unix_groups_domain_name()); type = SID_NAME_DOM_GRP; goto ok; } /* * Ok, all possibilities tried. Fail. */ TALLOC_FREE(tmp_ctx); return false; ok: if ((domain == NULL) || (name == NULL)) { DEBUG(0, ("talloc failed\n")); TALLOC_FREE(tmp_ctx); return false; } /* * Hand over the results to the talloc context we've been given. */ if ((ret_name != NULL) && !(*ret_name = talloc_strdup(mem_ctx, name))) { DEBUG(0, ("talloc failed\n")); TALLOC_FREE(tmp_ctx); return false; } if (ret_domain != NULL) { char *tmp_dom; if (!(tmp_dom = talloc_strdup(mem_ctx, domain))) { DEBUG(0, ("talloc failed\n")); TALLOC_FREE(tmp_ctx); return false; } strupper_m(tmp_dom); *ret_domain = tmp_dom; } if (ret_sid != NULL) { sid_copy(ret_sid, &sid); } if (ret_type != NULL) { *ret_type = type; } TALLOC_FREE(tmp_ctx); return true; }
static void popt_common_credentials_callback(poptContext con, enum poptCallbackReason reason, const struct poptOption *opt, const char *arg, const void *data) { struct user_auth_info *auth_info = talloc_get_type_abort( *((const char **)data), struct user_auth_info); char *p; if (reason == POPT_CALLBACK_REASON_PRE) { set_cmdline_auth_info_username(auth_info, "GUEST"); if (getenv("LOGNAME")) { set_cmdline_auth_info_username(auth_info, getenv("LOGNAME")); } if (getenv("USER")) { char *puser = SMB_STRDUP(getenv("USER")); if (!puser) { exit(ENOMEM); } set_cmdline_auth_info_username(auth_info, puser); if ((p = strchr_m(puser,'%'))) { size_t len; *p = 0; len = strlen(p+1); set_cmdline_auth_info_password(auth_info, p+1); memset(strchr_m(getenv("USER"),'%')+1,'X',len); } SAFE_FREE(puser); } if (getenv("PASSWD")) { set_cmdline_auth_info_password(auth_info, getenv("PASSWD")); } if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) { get_password_file(auth_info); } return; } switch(opt->val) { case 'U': { char *lp; char *puser = SMB_STRDUP(arg); if ((lp=strchr_m(puser,'%'))) { size_t len; *lp = 0; set_cmdline_auth_info_username(auth_info, puser); set_cmdline_auth_info_password(auth_info, lp+1); len = strlen(lp+1); memset(strchr_m(arg,'%')+1,'X',len); } else { set_cmdline_auth_info_username(auth_info, puser); } SAFE_FREE(puser); } break; case 'A': get_credentials_file(auth_info, arg); break; case 'k': #ifndef HAVE_KRB5 d_printf("No kerberos support compiled in\n"); exit(1); #else set_cmdline_auth_info_use_krb5_ticket(auth_info); #endif break; case 'S': if (!set_cmdline_auth_info_signing_state(auth_info, arg)) { fprintf(stderr, "Unknown signing option %s\n", arg ); exit(1); } break; case 'P': set_cmdline_auth_info_use_machine_account(auth_info); break; case 'N': set_cmdline_auth_info_password(auth_info, ""); break; case 'e': set_cmdline_auth_info_smb_encrypt(auth_info); break; } }
bool vfs_init_custom(connection_struct *conn, const char *vfs_object) { char *module_path = NULL; char *module_name = NULL; char *module_param = NULL, *p; vfs_handle_struct *handle; const struct vfs_init_function_entry *entry; if (!conn||!vfs_object||!vfs_object[0]) { DEBUG(0,("vfs_init_custon() called with NULL pointer or emtpy vfs_object!\n")); return False; } if(!backends) { static_init_vfs; } DEBUG(3, ("Initialising custom vfs hooks from [%s]\n", vfs_object)); module_path = smb_xstrdup(vfs_object); p = strchr_m(module_path, ':'); if (p) { *p = 0; module_param = p+1; trim_char(module_param, ' ', ' '); } trim_char(module_path, ' ', ' '); module_name = smb_xstrdup(module_path); if ((module_name[0] == '/') && (strcmp(module_path, DEFAULT_VFS_MODULE_NAME) != 0)) { /* * Extract the module name from the path. Just use the base * name of the last path component. */ SAFE_FREE(module_name); module_name = smb_xstrdup(strrchr_m(module_path, '/')+1); p = strchr_m(module_name, '.'); if (p != NULL) { *p = '\0'; } } /* First, try to load the module with the new module system */ entry = vfs_find_backend_entry(module_name); if (!entry) { NTSTATUS status; DEBUG(5, ("vfs module [%s] not loaded - trying to load...\n", vfs_object)); status = smb_probe_module("vfs", module_path); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("error probing vfs module '%s': %s\n", module_path, nt_errstr(status))); goto fail; } entry = vfs_find_backend_entry(module_name); if (!entry) { DEBUG(0,("Can't find a vfs module [%s]\n",vfs_object)); goto fail; } } DEBUGADD(5,("Successfully loaded vfs module [%s] with the new modules system\n", vfs_object)); handle = TALLOC_ZERO_P(conn, vfs_handle_struct); if (!handle) { DEBUG(0,("TALLOC_ZERO() failed!\n")); goto fail; } handle->conn = conn; handle->fns = entry->fns; if (module_param) { handle->param = talloc_strdup(conn, module_param); } DLIST_ADD(conn->vfs_handles, handle); SAFE_FREE(module_path); SAFE_FREE(module_name); return True; fail: SAFE_FREE(module_path); SAFE_FREE(module_name); return False; }
int /* O - Exit status */ main(int argc, /* I - Number of command-line arguments */ char *argv[]) /* I - Command-line arguments */ { int i; /* Looping var */ int copies; /* Number of copies */ char uri[1024], /* URI */ *sep, /* Pointer to separator */ *password; /* Password */ const char *username, /* Username */ *server, /* Server name */ *printer; /* Printer name */ const char *workgroup; /* Workgroup */ FILE *fp; /* File to print */ int status=0; /* Status of LPD job */ struct cli_state *cli; /* SMB interface */ /* we expect the URI in argv[0]. Detect the case where it is in argv[1] and cope */ if (argc > 2 && strncmp(argv[0],"smb://", 6) && !strncmp(argv[1],"smb://", 6)) { argv++; argc--; } if (argc == 1) { /* * NEW! In CUPS 1.1 the backends are run with no arguments to list the * available devices. These can be devices served by this backend * or any other backends (i.e. you can have an SNMP backend that * is only used to enumerate the available network printers... :) */ list_devices(); return (0); } if (argc < 6 || argc > 7) { fprintf(stderr, "Usage: %s [DEVICE_URI] job-id user title copies options [file]\n", argv[0]); fputs(" The DEVICE_URI environment variable can also contain the\n", stderr); fputs(" destination printer:\n", stderr); fputs("\n", stderr); fputs(" smb://[username:password@][workgroup/]server/printer\n", stderr); return (1); } /* * If we have 7 arguments, print the file named on the command-line. * Otherwise, print data from stdin... */ if (argc == 6) { /* * Print from Copy stdin to a temporary file... */ fp = stdin; copies = 1; } else if ((fp = fopen(argv[6], "rb")) == NULL) { perror("ERROR: Unable to open print file"); return (1); } else copies = atoi(argv[4]); /* * Find the URI... */ if (strncmp(argv[0], "smb://", 6) == 0) strncpy(uri, argv[0], sizeof(uri) - 1); else if (getenv("DEVICE_URI") != NULL) strncpy(uri, getenv("DEVICE_URI"), sizeof(uri) - 1); else { fputs("ERROR: No device URI found in argv[0] or DEVICE_URI environment variable!\n", stderr); return (1); } uri[sizeof(uri) - 1] = '\0'; /* * Extract the destination from the URI... */ if ((sep = strrchr_m(uri, '@')) != NULL) { username = uri + 6; *sep++ = '\0'; server = sep; /* * Extract password as needed... */ if ((password = strchr_m(username, ':')) != NULL) *password++ = '\0'; else password = ""; } else { username = ""; password = ""; server = uri + 6; } if ((sep = strchr_m(server, '/')) == NULL) { fputs("ERROR: Bad URI - need printer name!\n", stderr); return (1); } *sep++ = '\0'; printer = sep; if ((sep = strchr_m(printer, '/')) != NULL) { /* * Convert to smb://[username:password@]workgroup/server/printer... */ *sep++ = '\0'; workgroup = server; server = printer; printer = sep; } else workgroup = NULL; /* * Setup the SAMBA server state... */ setup_logging("smbspool", True); in_client = True; /* Make sure that we tell lp_load we are */ if (!lp_load(dyn_CONFIGFILE, True, False, False)) { fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE); return (1); } if (workgroup == NULL) workgroup = lp_workgroup(); load_interfaces(); do { if ((cli = smb_connect(workgroup, server, printer, username, password)) == NULL) { if (getenv("CLASS") == NULL) { fprintf(stderr, "ERROR: Unable to connect to SAMBA host, will retry in 60 seconds..."); sleep (60); } else { fprintf(stderr, "ERROR: Unable to connect to SAMBA host, trying next printer..."); return (1); } } } while (cli == NULL); /* * Now that we are connected to the server, ignore SIGTERM so that we * can finish out any page data the driver sends (e.g. to eject the * current page... Only ignore SIGTERM if we are printing data from * stdin (otherwise you can't cancel raw jobs...) */ if (argc < 7) CatchSignal(SIGTERM, SIG_IGN); /* * Queue the job... */ for (i = 0; i < copies; i ++) if ((status = smb_print(cli, argv[3] /* title */, fp)) != 0) break; cli_shutdown(cli); /* * Return the queue status... */ return (status); }