/* Prompt for a password and set it on the SMB context. */ static Boolean SMBPasswordPrompt( SMBHANDLE hConnection, uint64_t options) { void * hContext = NULL; NTSTATUS status; char * passwd; char passbuf[SMB_MAXPASSWORDLEN + 1]; char prompt[128]; /* They told us not to prompt */ if (options & kSMBOptionNoPrompt) return false; status = SMBServerContext(hConnection, &hContext); if (!NT_SUCCESS(status)) { return false; } /* * If the password is already set, don't prompt. Since anonymous and guest * both have an empty password this will protect us from prompting in * those cases. */ if (((struct smb_ctx *)hContext)->ct_flags & SMBCF_EXPLICITPWD) { return false; } /* * If the target hasn't set a user name, let's assume that we should use * the current username. This is almost always what the caller wants, when * being prompted for a password. The above check protect us from overriding * anonymouse connections. */ if (((struct smb_ctx *)hContext)->ct_setup.ioc_user[0] == '\0') { struct passwd * pwent; pwent = getpwuid(geteuid()); if (pwent) { smb_ctx_setuser(hContext, pwent->pw_name); } } snprintf(prompt, sizeof(prompt), "Password for %s: ", ((struct smb_ctx *)hContext)->serverName); /* If we have a TTY, read a password and retry ... */ passwd = readpassphrase(prompt, passbuf, sizeof(passbuf), RPP_REQUIRE_TTY); if (passwd) { smb_ctx_setpassword(hContext, passwd, TRUE); memset(passbuf, 0, sizeof(passbuf)); } return true; }
/* * Need to call SetWorkgroupFromURL just in case we have a workgroup name. CFURL does not handle * a CIFS style URL with a workgroup name. */ static int SetUserNameFromURL(struct smb_ctx *ctx, CFURLRef url) { CFStringRef userNameRef = SetWorkgroupFromURL(ctx, url); char username[SMB_MAXUSERNAMELEN+1]; int error; /* No user name in the URL */ if (! userNameRef) return 0; LogCFString(userNameRef, "Username",__FUNCTION__, __LINE__); /* Conversion failed or the data doesn't fit in the buffer */ if (CFStringGetCString(userNameRef, username, SMB_MAXUSERNAMELEN+1, kCFStringEncodingUTF8) == FALSE) { error = ENAMETOOLONG; /* Not sure what else to return. */ } else { error = smb_ctx_setuser(ctx, username); } CFRelease(userNameRef); return error; }
NTSTATUS SMBOpenServerWithMountPoint( const char * pTargetMountPath, const char * pTargetTreeName, SMBHANDLE * outConnection, uint64_t options) { NTSTATUS status; int err; void * hContext; struct statfs statbuf; *outConnection = NULL; status = SMBLibraryInit(); if (!NT_SUCCESS(status)) { goto done; } /* Need to get the mount from name, use that as the URL */ err = statfs(pTargetMountPath, &statbuf); if (err) { status = STATUS_OBJECT_PATH_NOT_FOUND; goto done; } status = SMBAllocateServer(outConnection, statbuf.f_mntfromname); if (!NT_SUCCESS(status)) { goto done; } status = SMBServerContext(*outConnection, &hContext); if (!NT_SUCCESS(status)) { goto done; } /* Need to clear out the user name field */ smb_ctx_setuser(hContext, ""); err = findMountPointVC(hContext, pTargetMountPath); if (err) { status = STATUS_OBJECT_NAME_NOT_FOUND; errno = err; goto done; } if (options & kSMBOptionSessionOnly) { goto done; } /* No tree name, let's assume that the caller means IPC$ */ if (!pTargetTreeName) { pTargetTreeName = "IPC$"; } err = smb_ctx_setshare(hContext, pTargetTreeName); if (err) { if (err == ENAMETOOLONG) { status = STATUS_NAME_TOO_LONG; } else { status = STATUS_NO_MEMORY; } errno = err; goto done; } /* OK, now we have a virtual circuit but no tree connection yet. */ err = smb_share_connect((*outConnection)->context); if (err) { status = STATUS_BAD_NETWORK_NAME; errno = err; goto done; } status = STATUS_SUCCESS; done: if ((!NT_SUCCESS(status)) && *outConnection) { SMBReleaseServer(*outConnection); *outConnection = NULL; } return status; }
/* * Prescan command line for [-U user] argument * and fill context with defaults */ int smb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[], int minlevel, int maxlevel, int sharetype) { int opt, error = 0; uid_t euid; const char *arg, *cp; struct passwd *pwd; bzero(ctx,sizeof(*ctx)); error = nb_ctx_create(&ctx->ct_nb); if (error) return error; ctx->ct_fd = -1; ctx->ct_parsedlevel = SMBL_NONE; ctx->ct_minlevel = minlevel; ctx->ct_maxlevel = maxlevel; ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE; ctx->ct_ssn.ioc_timeout = 15; ctx->ct_ssn.ioc_retrycount = 4; ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER; ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP; ctx->ct_ssn.ioc_mode = SMBM_EXEC; ctx->ct_ssn.ioc_rights = SMBM_DEFAULT; ctx->ct_sh.ioc_opt = SMBVOPT_CREATE; ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER; ctx->ct_sh.ioc_group = SMBM_ANY_GROUP; ctx->ct_sh.ioc_mode = SMBM_EXEC; ctx->ct_sh.ioc_rights = SMBM_DEFAULT; ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER; ctx->ct_sh.ioc_group = SMBM_ANY_GROUP; nb_ctx_setscope(ctx->ct_nb, ""); euid = geteuid(); if ((pwd = getpwuid(euid)) != NULL) { smb_ctx_setuser(ctx, pwd->pw_name); endpwent(); } else if (euid == 0) smb_ctx_setuser(ctx, "root"); else return 0; if (argv == NULL) return 0; for (opt = 1; opt < argc; opt++) { cp = argv[opt]; if (strncmp(cp, "//", 2) != 0) continue; error = smb_ctx_parseunc(ctx, cp, sharetype, (const char**)&cp); if (error) return error; ctx->ct_uncnext = cp; break; } while (error == 0 && (opt = cf_getopt(argc, argv, ":E:L:U:")) != -1) { arg = cf_optarg; switch (opt) { case 'E': error = smb_ctx_setcharset(ctx, arg); if (error) return error; break; case 'L': error = nls_setlocale(optarg); if (error) break; break; case 'U': error = smb_ctx_setuser(ctx, arg); break; } } cf_optind = cf_optreset = 1; return error; }
/* * Here we expect something like "[proto:]//[user@]host[/share][/path]" */ int smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype, const char **next) { const char *p = unc; char *p1; char tmp[1024]; int error ; ctx->ct_parsedlevel = SMBL_NONE; if (*p++ != '/' || *p++ != '/') { smb_error("UNC should start with '//'", 0); return EINVAL; } p1 = tmp; error = getsubstring(p, '@', p1, sizeof(tmp), &p); if (!error) { if (ctx->ct_maxlevel < SMBL_VC) { smb_error("no user name required", 0); return EINVAL; } if (*p1 == 0) { smb_error("empty user name", 0); return EINVAL; } error = smb_ctx_setuser(ctx, tmp); if (error) return error; ctx->ct_parsedlevel = SMBL_VC; } error = getsubstring(p, '/', p1, sizeof(tmp), &p); if (error) { error = getsubstring(p, '\0', p1, sizeof(tmp), &p); if (error) { smb_error("no server name found", 0); return error; } } if (*p1 == 0) { smb_error("empty server name", 0); return EINVAL; } error = smb_ctx_setserver(ctx, tmp); if (error) return error; if (sharetype == SMB_ST_NONE) { *next = p; return 0; } if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) { smb_error("no share name required", 0); return EINVAL; } error = getsubstring(p, '/', p1, sizeof(tmp), &p); if (error) { error = getsubstring(p, '\0', p1, sizeof(tmp), &p); if (error) { smb_error("unexpected end of line", 0); return error; } } if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE) { smb_error("empty share name", 0); return EINVAL; } *next = p; if (*p1 == 0) return 0; error = smb_ctx_setshare(ctx, p1, sharetype); return error; }
/* * Setup a new SMB client context. * * Get the SMB server's configuration stuff and * store it in the new client context object. */ int smbrdr_ctx_new(struct smb_ctx **ctx_p, char *server, char *domain, char *user) { struct smb_ctx *ctx = NULL; uchar_t nthash[SMBAUTH_HASH_SZ]; int64_t lmcl; int authflags, err; assert(server != NULL); assert(domain != NULL); assert(user != NULL); if ((err = smb_ctx_alloc(&ctx)) != 0) return (NT_STATUS_NO_MEMORY); /* * Set server, share, domain, user * (in the ctx handle). */ (void) smb_ctx_setfullserver(ctx, server); (void) smb_ctx_setshare(ctx, "IPC$", USE_IPC); (void) smb_ctx_setdomain(ctx, domain, B_TRUE); (void) smb_ctx_setuser(ctx, user, B_TRUE); /* * Set auth. info (hash) and type. */ if (user[0] == '\0') { authflags = SMB_AT_ANON; } else { (void) smb_config_getnum(SMB_CI_LM_LEVEL, &lmcl); if (lmcl <= 2) { /* Send NTLM */ authflags = SMB_AT_NTLM1; } else { /* Send NTLMv2 */ authflags = SMB_AT_NTLM2; } smb_ipc_get_passwd(nthash, sizeof (nthash)); (void) smb_ctx_setpwhash(ctx, nthash, NULL); } (void) smb_ctx_setauthflags(ctx, authflags); /* * Do lookup, connect, session setup, tree connect. * Or find and reuse a session/tree, if one exists. */ if ((err = smb_ctx_resolve(ctx)) != 0) { err = NT_STATUS_BAD_NETWORK_PATH; goto errout; } if ((err = smb_ctx_get_ssn(ctx)) != 0) { err = NT_STATUS_NETWORK_ACCESS_DENIED; goto errout; } if ((err = smb_ctx_get_tree(ctx)) != 0) { err = NT_STATUS_BAD_NETWORK_NAME; goto errout; } /* Success! */ *ctx_p = ctx; return (0); errout: smb_ctx_free(ctx); return (err); }
/* * Here we expect something like "[proto:]//[user@]host[:psmb[:pnb]][/share][/path]" */ int smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype, const char **next) { const char *p = unc; char *p1, *psmb, *pnb; char tmp[1024]; int error ; ctx->ct_parsedlevel = SMBL_NONE; if (*p++ != '/' || *p++ != '/') { smb_error("UNC should start with '//'", 0); return EINVAL; } p1 = tmp; error = getsubstring(p, '@', p1, sizeof(tmp), &p); if (!error) { if (ctx->ct_maxlevel < SMBL_VC) { smb_error("no user name required", 0); return EINVAL; } if (*p1 == 0) { smb_error("empty user name", 0); return EINVAL; } error = smb_ctx_setuser(ctx, tmp); if (error) return error; ctx->ct_parsedlevel = SMBL_VC; } error = getsubstring(p, '/', p1, sizeof(tmp), &p); if (error) { error = getsubstring(p, '\0', p1, sizeof(tmp), &p); if (error) { smb_error("no server name found", 0); return error; } } if (*p1 == 0) { smb_error("empty server name", 0); return EINVAL; } /* * Check for port number specification. */ psmb = strchr(tmp, ':'); if (psmb) { *psmb++ = '\0'; pnb = strchr(psmb, ':'); if (pnb) { *pnb++ = '\0'; error = smb_ctx_setnbport(ctx, atoi(pnb)); if (error) { smb_error("Invalid NetBIOS port number", 0); return error; } } error = smb_ctx_setsmbport(ctx, atoi(psmb)); if (error) { smb_error("Invalid SMB port number", 0); return error; } } error = smb_ctx_setserver(ctx, tmp); if (error) return error; if (sharetype == SMB_ST_NONE) { *next = p; return 0; } if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) { smb_error("no share name required", 0); return EINVAL; } error = getsubstring(p, '/', p1, sizeof(tmp), &p); if (error) { error = getsubstring(p, '\0', p1, sizeof(tmp), &p); if (error) { smb_error("unexpected end of line", 0); return error; } } if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE) { smb_error("empty share name", 0); return EINVAL; } *next = p; if (*p1 == 0) return 0; error = smb_ctx_setshare(ctx, p1, sharetype); return error; }